2 * UDP Gateway utility network layer
3 * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <glib/gmessages.h>
23 #include <glib/gmain.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <glib/glist.h>
29 #include <sys/types.h>
32 #include <glib/giochannel.h>
33 #include <sys/ioctl.h>
35 #include <glib/galloca.h>
36 #include <glib/gprintf.h>
37 #include <glib/grand.h>
44 #define NETWORK_PATHNAME_PID "/var/run/udpgate.pid"
45 #define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN) /* |G_IO_PRI */
46 #define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS) /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
47 #define SERVER_INADDR 0x7F000001
49 #define SERVER_INADDR 0x511F02EA /* paulina.vellum.cz = 81.31.2.234; host order */
50 #define SERVER_PORT 9201 /* host order */
51 #define PROBE_INADDR SERVER_INADDR /* host order */
52 #define PROBE_PORT 8201 /* host order */
53 #define CLIENT_TIMEOUT_SEC (5*60)
54 #define PROBE_TIMEOUT_SEC (5)
57 void (*network_notify_hostip)(guint32 hostip_guint32);
62 struct sockaddr_in sockaddr_in_from;
66 static GSource *sock_gsource;
68 static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */
69 static struct client *master; /* no 'timeout' permitted */
70 static struct client *probe; /* 'timeout' permitted */
71 static guint32 probe_unique;
73 static gboolean write_daemon_running(pid_t pid);
75 pid_t is_daemon_running(void)
78 char buf[LINE_MAX],*got;
84 if (!(f=fopen(NETWORK_PATHNAME_PID,"r")))
86 got=fgets(buf,sizeof(buf),f);
87 fclose(f); /* FIXME: error ignored */
90 write_daemon_running((pid_t)-1); /* unlink */
97 if (kill((pid_t)pid_int,0)) {
102 return (pid_t)pid_int;
105 static gboolean write_daemon_running(pid_t pid)
109 if (pid==(pid_t)-1) {
110 if (unlink(NETWORK_PATHNAME_PID)) {
112 g_warning(_("Error removing PID file \"%s\": %m"),NETWORK_PATHNAME_PID);
117 if (!(f=fopen(NETWORK_PATHNAME_PID,"w"))) {
118 g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,NETWORK_PATHNAME_PID);
121 fprintf(f,"%d\n",(int)pid); /* errors ignored */
122 fclose(f); /* FIXME: error ignored */
126 static void client_timeout_remove(struct client *client)
128 g_return_if_fail(client!=NULL);
129 g_return_if_fail(client!=master);
131 if (client->timeout_id) {
132 gboolean errgboolean;
134 errgboolean=g_source_remove(client->timeout_id);
135 g_assert(errgboolean==TRUE);
136 client->timeout_id=0;
140 static gboolean client_touch_timeout(struct client *client);
142 static void client_touch(struct client *client)
144 g_return_if_fail(client!=NULL);
145 g_return_if_fail(client!=master);
147 client_timeout_remove(client);
148 client->timeout_id=g_timeout_add(
149 (client==probe ? PROBE_TIMEOUT_SEC*1000 : CLIENT_TIMEOUT_SEC*1000), /* interval; msec */
150 (GSourceFunc)client_touch_timeout, /* function */
152 g_assert(client->timeout_id!=0);
155 static void client_destroy(struct client *client);
157 static gboolean client_touch_timeout(struct client *client)
159 g_return_val_if_fail(client!=NULL,FALSE); /* FALSE=>should be removed */
160 g_return_val_if_fail(client!=master,FALSE); /* FALSE=>should be removed */
164 if (network_notify_hostip)
165 (*network_notify_hostip)(0);
168 client_destroy(client);
170 return FALSE; /* GSource should be removed */
173 static void handle_master_probe(const void *packet,size_t gotlen,const struct sockaddr_in *sockaddr_in_from)
175 GHashTable *got_hash;
176 gpointer got_unique_gpointer;
177 gpointer hostip_gpointer;
178 guint32 hostip_guint32;
180 g_return_if_fail(packet!=NULL);
181 g_return_if_fail(sockaddr_in_from!=NULL);
186 if (!(got_hash=packet_disassembly(packet,gotlen)))
188 if (!(g_hash_table_lookup_extended(
189 got_hash, /* hash_table */
190 GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32), /* lookup_key */
192 &got_unique_gpointer))) {
193 err_packet_disassembly_destroy_got_hash:
194 packet_disassembly_destroy(got_hash);
197 if (GPOINTER_TO_UINT(got_unique_gpointer)!=probe_unique)
198 goto err_packet_disassembly_destroy_got_hash;
199 if (!(g_hash_table_lookup_extended(
200 got_hash, /* hash_table */
201 GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_INADDR), /* lookup_key */
204 goto err_packet_disassembly_destroy_got_hash;
205 hostip_guint32=GPOINTER_TO_UINT(hostip_gpointer);
206 packet_disassembly_destroy(got_hash);
208 client_destroy(probe);
209 if (network_notify_hostip)
210 (*network_notify_hostip)(hostip_guint32);
213 static struct client *client_new(void);
215 static void handle_master(struct client *master)
218 struct sockaddr_in sockaddr_in_from;
219 char packet[0x10000];
220 struct client *client;
221 struct sockaddr_in sockaddr_in_server;
222 socklen_t sockaddr_in_from_length;
224 g_return_if_fail(master!=NULL);
226 sockaddr_in_from_length=sizeof(sockaddr_in_from);
227 while (-1!=(gotlen=recvfrom(
228 master->gpollfd.fd, /* s */
230 sizeof(packet), /* len */
232 (struct sockaddr *)&sockaddr_in_from, /* from */
233 &sockaddr_in_from_length))) { /* fromlen */
236 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
239 if (packet_recognized(packet,gotlen)) {
240 handle_master_probe(packet,gotlen,&sockaddr_in_from);
244 /* FIXME: Performance: Ugly search... */
245 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
246 client=clientl->data;
250 && client->sockaddr_in_from.sin_family ==sockaddr_in_from.sin_family
251 && client->sockaddr_in_from.sin_port ==sockaddr_in_from.sin_port
252 && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr)
257 client->sockaddr_in_from=sockaddr_in_from;
259 client_touch(client);
260 UDPGATE_MEMZERO(&sockaddr_in_server);
261 sockaddr_in_server.sin_family=AF_INET;
262 sockaddr_in_server.sin_port=htons(SERVER_PORT);
263 sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR);
264 /* FIXME: errors checking */
266 client->gpollfd.fd, /* s */
270 (struct sockaddr *)&sockaddr_in_server, /* to */
271 sizeof(sockaddr_in_server)); /* tolen */
275 static void handle_probe(struct client *probe)
278 struct sockaddr_in sockaddr_in_from;
279 char packet[0x10000];
280 socklen_t sockaddr_in_from_length;
282 g_return_if_fail(probe!=NULL);
284 sockaddr_in_from_length=sizeof(sockaddr_in_from);
285 while (-1!=(gotlen=recvfrom(
286 master->gpollfd.fd, /* s */
288 sizeof(packet), /* len */
290 (struct sockaddr *)&sockaddr_in_from, /* from */
291 &sockaddr_in_from_length))) { /* fromlen */
292 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
295 /* Probe socket should have no response; maybe some ICMP errors - ignored. */
299 static void handle_client(struct client *client)
302 struct sockaddr_in sockaddr_in_from;
303 char packet [0x10000];
304 socklen_t sockaddr_in_from_length;
306 g_return_if_fail(client!=NULL);
307 g_return_if_fail(master!=NULL);
309 while (-1!=(gotlen=recvfrom(
310 client->gpollfd.fd, /* s */
312 sizeof(packet), /* len */
314 (struct sockaddr *)&sockaddr_in_from, /* from */
315 &sockaddr_in_from_length))) { /* fromlen */
316 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
318 client_touch(client);
319 /* FIXME: errors checking */
321 master->gpollfd.fd, /* s */
325 (struct sockaddr *)&client->sockaddr_in_from, /* to */
326 sizeof(client->sockaddr_in_from)); /* tolen */
330 static gboolean sock_source_callback(gpointer data /* unused */)
334 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
335 struct client *client=clientl->data;
337 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
338 /**/ if (client==master)
339 handle_master(client);
340 else if (client==probe)
341 handle_probe(client);
343 handle_client(client);
347 return TRUE; /* the source should be kept active */
350 static gboolean sock_source_prepare(GSource *source,gint *timeout)
357 static gboolean sock_source_check(GSource *source)
361 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
362 struct client *client=clientl->data;
364 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS)
370 static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
372 g_assert(callback!=NULL);
373 return (*callback)(user_data);
376 static GSourceFuncs sock_source_watch_funcs={
379 sock_source_dispatch,
383 static void sock_gsource_destroy(void)
385 while (sock_client_list)
386 client_destroy(sock_client_list->data);
388 g_assert(master==NULL);
391 g_source_destroy(sock_gsource);
395 write_daemon_running((pid_t)-1); /* unlink */
398 static gboolean sock_gsource_new(void)
403 g_assert(sock_client_list==NULL);
405 /* attach sock_source_callback() to watch for any abnormalities
406 * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
408 if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) {
409 g_warning("g_source_new(): %m");
412 g_source_set_callback(
413 sock_gsource, /* source */
414 sock_source_callback, /* func */
417 if (!g_source_attach( /* returns 'guint' id */
418 sock_gsource, /* source */
419 NULL)) { /* context; NULL means 'default context' */
420 g_warning("g_source_attach(gsource,NULL): %m");
421 sock_gsource_destroy();
427 static struct client *client_new(void)
429 struct client *client;
430 static unsigned long oneul=1;
433 if (!sock_gsource_new())
436 if (-1==(sock=socket(AF_INET,SOCK_DGRAM,0))) {
437 g_warning("socket(AF_INET,SOCK_DGRAM): %m");
440 if (ioctl(sock,FIONBIO,&oneul)) { /* non-blocking mode */
441 g_warning("ioctl(sock,FIONBIO,&1): %m");
442 close(sock); /* errors ignored */
447 client->gpollfd.fd=sock;
448 client->gpollfd.events=SOCK_SOURCE_CHECK_EVENTS;
449 client->gpollfd.revents=0;
450 client->timeout_id=0;
451 sock_client_list=g_list_prepend(sock_client_list,client);
452 g_source_add_poll(sock_gsource,&client->gpollfd);
457 static void client_destroy(struct client *client)
459 g_return_if_fail(client!=NULL);
461 if (!sock_gsource_new())
464 if (client==master) {
466 g_assert(client->timeout_id==0);
471 client_timeout_remove(client);
474 g_source_remove_poll(sock_gsource,&client->gpollfd);
475 sock_client_list=g_list_remove(sock_client_list,client);
476 close(client->gpollfd.fd); /* errors ignored */
480 static gboolean probe_send(struct client *probe,gint port_local)
482 struct sockaddr_in sockaddr_in_server;
483 GHashTable *probe_hash;
485 size_t packet_length;
487 g_return_val_if_fail(probe!=NULL,FALSE);
489 probe_unique=g_random_int();
491 probe_hash=g_hash_table_new(
492 g_direct_hash, /* hash_func */
493 g_direct_equal); /* key_equal_func */
494 g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_PORT) ,GUINT_TO_POINTER(port_local));
495 g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),GUINT_TO_POINTER(probe_unique));
496 packet=packet_assembly(&packet_length,probe_hash);
497 g_hash_table_destroy(probe_hash);
501 UDPGATE_MEMZERO(&sockaddr_in_server);
502 sockaddr_in_server.sin_family=AF_INET;
503 sockaddr_in_server.sin_port=htons(PROBE_PORT);
504 sockaddr_in_server.sin_addr.s_addr=htonl(PROBE_INADDR);
505 /* FIXME: errors checking */
507 probe->gpollfd.fd, /* s */
509 packet_length, /* len */
511 (struct sockaddr *)&sockaddr_in_server, /* to */
512 sizeof(sockaddr_in_server)); /* tolen */
517 gboolean network_start(gint port)
520 struct sockaddr_in sockaddr_in;
522 g_return_val_if_fail(port>=0,FALSE);
524 if ((pid_t)-1!=(daemon_pid=is_daemon_running())) {
525 g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
529 /* Setup 'master': */
530 g_assert(master==NULL);
531 if (!(master=client_new()))
533 UDPGATE_MEMZERO(&sockaddr_in);
534 sockaddr_in.sin_family=AF_INET;
535 sockaddr_in.sin_port=htons(port);
536 sockaddr_in.sin_addr.s_addr=htonl(INADDR_ANY);
537 if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
538 g_warning("bind(sock,{AF_INET,INADDR_ANY:%d}): %m",(int)port);
539 err_sock_gsource_destroy:
540 sock_gsource_destroy();
545 if (!(probe=client_new()))
546 goto err_sock_gsource_destroy;
547 probe_send(probe,port);
548 client_touch(probe); /* timeout */
550 write_daemon_running(getpid());
554 gboolean network_stop(void)
559 if ((pid_t)-1==(daemon_pid=is_daemon_running())) {
560 g_warning(_("Cannot stop network daemon: Daemon is not running"));
563 if (daemon_pid==getpid()) {
564 sock_gsource_destroy();
568 kill(daemon_pid,SIGKILL);
571 g_warning(udpgate_printf_alloca(_("Unable to stop the daemon at PID %d: %s"),
572 (int)daemon_pid,strerror(errno_save)));