2 * @file purple-network.c
6 * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "config.h" /* coverity[hfa: FALSE] */
31 #include "conversation.h"
33 #include "eventloop.h"
36 /* wrappers for write() & friends for socket handling */
37 #include "win32/win32dep.h"
40 #include <sys/types.h>
41 #include <sys/ioctl.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #ifdef HAVE_SYS_SOCKIO_H
47 #include <sys/sockio.h> /* SIOCGIFCONF for Solaris */
55 #include "sipe-common.h"
56 #include "sipe-backend.h"
57 #include "purple-private.h"
61 * @TODO: get_suitable_local_ip()
63 * The code is most likely broken for Mac OS X as it seems that that platform
64 * returns variable-sized "struct ifreq". The new, alignment compliant code
65 * assumes a fix-sized "struct ifreq", i.e. it uses array access.
67 * If somebody is bothered by this, please provide a *VERIFIED* alternative
68 * implementation for platforms that define _SIZEOF_ADDR_IFREQ().
73 * Calling sizeof(struct ifreq) isn't always correct on
74 * Mac OS X (and maybe others).
76 #ifdef _SIZEOF_ADDR_IFREQ
77 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
79 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
86 * Returns local IP address suitable for connection.
88 * purple_network_get_my_ip() will not do this, because it might return an
89 * address within 169.254.x.x range that was assigned to interface disconnected
90 * from the network (when multiple network adapters are available). This is a
91 * copy-paste from libpurple's network.c, only change is that link local addresses
94 * Maybe this should be fixed in libpurple or some better solution found.
96 static const gchar
*get_suitable_local_ip(void)
98 int source
= socket(PF_INET
,SOCK_STREAM
, 0);
101 struct ifreq
*buffer
= g_new0(struct ifreq
, IFREQ_MAX
);
103 guint32 lhost
= htonl(127 * 256 * 256 * 256 + 1);
104 guint32 llocal
= htonl((169 << 24) + (254 << 16));
108 /* @TODO: assumes constant sizeof(struct ifreq) [see above] */
109 ifc
.ifc_len
= sizeof(struct ifreq
) * IFREQ_MAX
;
110 ifc
.ifc_req
= buffer
;
111 ioctl(source
, SIOCGIFCONF
, &ifc
);
115 for (i
= 0; i
< IFREQ_MAX
; i
++)
117 /* @TODO: assumes constant sizeof(struct ifreq) [see above] */
118 struct ifreq
*ifr
= &buffer
[i
];
120 if (ifr
->ifr_addr
.sa_family
== AF_INET
)
122 struct sockaddr_in sin
;
123 memcpy(&sin
, &ifr
->ifr_addr
, sizeof(struct sockaddr_in
));
124 if (sin
.sin_addr
.s_addr
!= lhost
125 && (sin
.sin_addr
.s_addr
& htonl(0xFFFF0000)) != llocal
)
127 long unsigned int add
= ntohl(sin
.sin_addr
.s_addr
);
128 g_snprintf(ip
, 16, "%lu.%lu.%lu.%lu",
145 const gchar
*sipe_backend_network_ip_address(SIPE_UNUSED_PARAMETER
struct sipe_core_public
*sipe_public
)
147 const gchar
*ip
= purple_network_get_my_ip(-1);
148 if (g_str_has_prefix(ip
, "169.254."))
149 ip
= get_suitable_local_ip();
153 struct sipe_backend_listendata
{
154 sipe_listen_start_cb listen_cb
;
155 sipe_client_connected_cb connect_cb
;
157 PurpleNetworkListenData
*listener
;
165 client_connected_cb(struct sipe_backend_listendata
*ldata
, gint listenfd
,
166 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
168 struct sockaddr_in saddr
;
169 socklen_t slen
= sizeof (saddr
);
171 int fd
= accept(listenfd
, (struct sockaddr
*)&saddr
, &slen
);
173 purple_input_remove(ldata
->watcher
);
176 ldata
->listenfd
= -1;
178 if (ldata
->connect_cb
) {
179 ldata
->connect_cb(sipe_backend_fd_from_int(fd
), ldata
->data
);
186 backend_listen_cb(int listenfd
, struct sipe_backend_listendata
*ldata
)
188 struct sockaddr_in addr
;
189 socklen_t socklen
= sizeof (addr
);
191 ldata
->listenfd
= -1;
192 ldata
->listener
= NULL
;
193 ldata
->listenfd
= listenfd
;
195 /* ignore error code */
196 (void) getsockname(listenfd
, (struct sockaddr
*)&addr
, &socklen
);
197 if (ldata
->listen_cb
)
198 ldata
->listen_cb(ntohs(addr
.sin_port
), ldata
->data
);
200 ldata
->watcher
= purple_input_add(listenfd
, PURPLE_INPUT_READ
,
201 (PurpleInputFunction
)client_connected_cb
,
205 struct sipe_backend_listendata
*
206 sipe_backend_network_listen_range(unsigned short port_min
,
207 unsigned short port_max
,
208 sipe_listen_start_cb listen_cb
,
209 sipe_client_connected_cb connect_cb
,
212 struct sipe_backend_listendata
*ldata
;
213 ldata
= g_new0(struct sipe_backend_listendata
, 1);
215 ldata
->listen_cb
= listen_cb
;
216 ldata
->connect_cb
= connect_cb
;
218 ldata
->listener
= purple_network_listen_range(port_min
, port_max
,
219 #if PURPLE_VERSION_CHECK(3,0,0)
220 /* @TODO: does FT work with IPv6? */
224 #if PURPLE_VERSION_CHECK(3,0,0)
225 /* @TODO: should we allow external mapping? */
228 (PurpleNetworkListenCallback
)backend_listen_cb
,
231 if (!ldata
->listener
) {
239 void sipe_backend_network_listen_cancel(struct sipe_backend_listendata
*ldata
)
241 g_return_if_fail(ldata
);
244 purple_network_listen_cancel(ldata
->listener
);
246 close(ldata
->listenfd
);
250 struct sipe_backend_fd
*
251 sipe_backend_fd_from_int(int fd
)
253 struct sipe_backend_fd
*sipe_fd
= g_new(struct sipe_backend_fd
, 1);
259 sipe_backend_fd_is_valid(struct sipe_backend_fd
*fd
)
261 return fd
&& fd
->fd
>= 0;
265 sipe_backend_fd_free(struct sipe_backend_fd
*fd
)