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>
41 #include "main.h" /* for optarg_verbose */
45 #define NETWORK_PATHNAME_PID "/var/run/udpgate.pid"
46 #define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN) /* |G_IO_PRI */
47 #define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS) /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
48 #define SERVER_INADDR 0x7F000001
50 #define SERVER_INADDR 0x511F02EA /* paulina.vellum.cz = 81.31.2.234; host order */
51 #define SERVER_PORT 9201 /* host order */
52 #define PROBE_INADDR SERVER_INADDR /* host order */
53 #define PROBE_PORT 8201 /* host order */
54 #define CLIENT_TIMEOUT_SEC (5*60)
55 #define PROBE_TIMEOUT_SEC (5)
58 void (*network_notify_hostip)(guint32 hostip_guint32);
63 struct sockaddr_in sockaddr_in_from;
67 static GSource *sock_gsource;
69 static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */
70 static struct client *master; /* no 'timeout' permitted */
71 static struct client *probe; /* 'timeout' permitted */
72 static guint32 probe_unique;
74 static gboolean write_daemon_running(pid_t pid);
76 pid_t is_daemon_running(void)
79 char buf[LINE_MAX],*got;
85 if (!(f=fopen(NETWORK_PATHNAME_PID,"r")))
87 got=fgets(buf,sizeof(buf),f);
88 fclose(f); /* FIXME: error ignored */
91 write_daemon_running((pid_t)-1); /* unlink */
98 if (kill((pid_t)pid_int,0)) {
103 return (pid_t)pid_int;
106 static gboolean write_daemon_running(pid_t pid)
110 if (pid==(pid_t)-1) {
111 if (unlink(NETWORK_PATHNAME_PID)) {
113 g_warning(_("Error removing PID file \"%s\": %m"),NETWORK_PATHNAME_PID);
118 if (!(f=fopen(NETWORK_PATHNAME_PID,"w"))) {
119 g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,NETWORK_PATHNAME_PID);
122 fprintf(f,"%d\n",(int)pid); /* errors ignored */
123 fclose(f); /* FIXME: error ignored */
127 static void client_timeout_remove(struct client *client)
129 g_return_if_fail(client!=NULL);
130 g_return_if_fail(client!=master);
132 if (client->timeout_id) {
133 gboolean errgboolean;
136 g_message(_("Client fd %d removed timeout id %d"),client->gpollfd.fd,client->timeout_id);
137 errgboolean=g_source_remove(client->timeout_id);
138 g_assert(errgboolean==TRUE);
139 client->timeout_id=0;
143 static gboolean client_touch_timeout(struct client *client);
145 static void client_touch(struct client *client)
147 g_return_if_fail(client!=NULL);
148 g_return_if_fail(client!=master);
150 client_timeout_remove(client);
151 client->timeout_id=g_timeout_add(
152 (client==probe ? PROBE_TIMEOUT_SEC*1000 : CLIENT_TIMEOUT_SEC*1000), /* interval; msec */
153 (GSourceFunc)client_touch_timeout, /* function */
156 g_message(_("Client fd %d new timeout id %d"),client->gpollfd.fd,client->timeout_id);
157 g_assert(client->timeout_id!=0);
160 static void client_destroy(struct client *client);
162 static gboolean client_touch_timeout(struct client *client)
164 g_return_val_if_fail(client!=NULL,FALSE); /* FALSE=>should be removed */
165 g_return_val_if_fail(client!=master,FALSE); /* FALSE=>should be removed */
168 g_message(_("Client fd %d timeout id %d occured/entered"),client->gpollfd.fd,client->timeout_id);
170 /* Do not destroy the timeout in client_destroy().
171 * It would crash GLib - we remove it be returning FALSE from here.
173 g_assert(client->timeout_id!=0);
174 client->timeout_id=0;
178 /* Never destroy 'client' now - it has been destroyed by network_stop()! */
179 g_warning(_("No probe response from the server, stopping the daemon. Please check your public Internet connectivity."));
182 client_destroy(client);
186 g_message(_("Client timeout occurance finish"));
188 return FALSE; /* GSource should be removed */
191 static void handle_master_probe(const void *packet,size_t gotlen,const struct sockaddr_in *sockaddr_in_from)
193 GHashTable *got_hash;
194 gpointer got_unique_gpointer;
195 gpointer hostip_gpointer;
196 guint32 hostip_guint32;
198 g_return_if_fail(packet!=NULL);
199 g_return_if_fail(sockaddr_in_from!=NULL);
204 if (!(got_hash=packet_disassembly(packet,gotlen)))
206 if (!(g_hash_table_lookup_extended(
207 got_hash, /* hash_table */
208 GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32), /* lookup_key */
210 &got_unique_gpointer))) {
211 err_packet_disassembly_destroy_got_hash:
212 packet_disassembly_destroy(got_hash);
215 if (GPOINTER_TO_UINT(got_unique_gpointer)!=probe_unique)
216 goto err_packet_disassembly_destroy_got_hash;
217 if (!(g_hash_table_lookup_extended(
218 got_hash, /* hash_table */
219 GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_INADDR), /* lookup_key */
222 goto err_packet_disassembly_destroy_got_hash;
223 hostip_guint32=GPOINTER_TO_UINT(hostip_gpointer);
224 packet_disassembly_destroy(got_hash);
226 client_destroy(probe);
227 if (network_notify_hostip)
228 (*network_notify_hostip)(hostip_guint32);
231 static struct client *client_new(void);
233 static void handle_master(struct client *master)
235 g_return_if_fail(master!=NULL);
238 char packet[0x10000];
240 struct sockaddr_in sockaddr_in_from;
241 struct client *client=NULL /* Prevent false positive: might be used uninitialized */;
242 struct sockaddr_in sockaddr_in_server;
243 socklen_t sockaddr_in_from_length;
246 sockaddr_in_from_length=sizeof(sockaddr_in_from);
247 if (-1==(gotlen=recvfrom(
248 master->gpollfd.fd, /* s */
250 sizeof(packet), /* len */
252 (struct sockaddr *)&sockaddr_in_from, /* from */
253 &sockaddr_in_from_length))) /* fromlen */
256 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
259 if (packet_recognized(packet,gotlen)) {
260 handle_master_probe(packet,gotlen,&sockaddr_in_from);
264 /* FIXME: Performance: Ugly search... */
265 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
266 client=clientl->data;
270 && client->sockaddr_in_from.sin_family ==sockaddr_in_from.sin_family
271 && client->sockaddr_in_from.sin_port ==sockaddr_in_from.sin_port
272 && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr)
277 client->sockaddr_in_from=sockaddr_in_from;
279 client_touch(client);
280 UDPGATE_MEMZERO(&sockaddr_in_server);
281 sockaddr_in_server.sin_family=AF_INET;
282 sockaddr_in_server.sin_port=htons(SERVER_PORT);
283 sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR);
284 /* FIXME: errors checking */
286 client->gpollfd.fd, /* s */
290 (struct sockaddr *)&sockaddr_in_server, /* to */
291 sizeof(sockaddr_in_server)); /* tolen */
295 static void handle_probe(struct client *probe)
297 g_return_if_fail(probe!=NULL);
301 struct sockaddr_in sockaddr_in_from;
302 char packet[0x10000];
303 socklen_t sockaddr_in_from_length;
305 sockaddr_in_from_length=sizeof(sockaddr_in_from);
306 if (-1==(gotlen=recvfrom(
307 master->gpollfd.fd, /* s */
309 sizeof(packet), /* len */
311 (struct sockaddr *)&sockaddr_in_from, /* from */
312 &sockaddr_in_from_length))) /* fromlen */
314 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
317 /* Probe socket should have no response; maybe some ICMP errors - ignored. */
321 static void handle_client(struct client *client)
323 g_return_if_fail(client!=NULL);
324 g_return_if_fail(master!=NULL);
328 struct sockaddr_in sockaddr_in_from;
329 char packet [0x10000];
330 socklen_t sockaddr_in_from_length;
332 sockaddr_in_from_length=sizeof(sockaddr_in_from);
333 if (-1==(gotlen=recvfrom(
334 client->gpollfd.fd, /* s */
336 sizeof(packet), /* len */
338 (struct sockaddr *)&sockaddr_in_from, /* from */
339 &sockaddr_in_from_length))) /* fromlen */
341 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
343 client_touch(client);
344 /* FIXME: errors checking */
346 master->gpollfd.fd, /* s */
350 (struct sockaddr *)&client->sockaddr_in_from, /* to */
351 sizeof(client->sockaddr_in_from)); /* tolen */
355 static gboolean sock_source_callback(gpointer data /* unused */)
359 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
360 struct client *client=clientl->data;
362 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
363 /**/ if (client==master)
364 handle_master(client);
365 else if (client==probe)
366 handle_probe(client);
368 handle_client(client);
372 return TRUE; /* the source should be kept active */
375 static gboolean sock_source_prepare(GSource *source,gint *timeout)
382 static gboolean sock_source_check(GSource *source)
386 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
387 struct client *client=clientl->data;
389 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS)
395 static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
397 g_assert(callback!=NULL);
398 return (*callback)(user_data);
401 static GSourceFuncs sock_source_watch_funcs={
404 sock_source_dispatch,
408 static void sock_gsource_destroy(void)
410 while (sock_client_list)
411 client_destroy(sock_client_list->data);
413 g_assert(master==NULL);
414 g_assert(probe==NULL);
417 g_source_destroy(sock_gsource);
421 write_daemon_running((pid_t)-1); /* unlink; errors ignored */
424 static gboolean sock_gsource_new(void)
429 g_assert(sock_client_list==NULL);
431 /* attach sock_source_callback() to watch for any abnormalities
432 * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
434 if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) {
435 g_warning("g_source_new(): %m");
438 g_source_set_callback(
439 sock_gsource, /* source */
440 sock_source_callback, /* func */
443 if (!g_source_attach( /* returns 'guint' id */
444 sock_gsource, /* source */
445 NULL)) { /* context; NULL means 'default context' */
446 g_warning("g_source_attach(gsource,NULL): %m");
447 sock_gsource_destroy();
453 static struct client *client_new(void)
455 struct client *client;
456 static unsigned long oneul=1;
459 if (!sock_gsource_new())
462 if (-1==(sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))) {
463 g_warning("socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP): %m");
466 if (ioctl(sock,FIONBIO,&oneul)) { /* non-blocking mode */
467 g_warning("ioctl(sock,FIONBIO,&1): %m");
468 close(sock); /* errors ignored */
473 client->gpollfd.fd=sock;
474 client->gpollfd.events=SOCK_SOURCE_CHECK_EVENTS;
475 client->gpollfd.revents=0;
476 client->timeout_id=0;
477 sock_client_list=g_list_prepend(sock_client_list,client);
478 g_source_add_poll(sock_gsource,&client->gpollfd);
481 g_message(_("Client fd %d created"),client->gpollfd.fd);
486 static void client_destroy(struct client *client)
488 g_return_if_fail(client!=NULL);
490 if (!sock_gsource_new())
494 g_message(_("Client fd %d timeout id %d destroy enter"),client->gpollfd.fd,client->timeout_id);
496 if (client==master) {
498 g_assert(client->timeout_id==0);
503 client_timeout_remove(client);
506 g_source_remove_poll(sock_gsource,&client->gpollfd);
507 sock_client_list=g_list_remove(sock_client_list,client);
508 close(client->gpollfd.fd); /* errors ignored */
511 g_message(_("Client fd %d timeout id %d destroy finish"),client->gpollfd.fd,client->timeout_id);
516 static gboolean probe_send(struct client *probe,gint port_local)
518 struct sockaddr_in sockaddr_in_server;
519 GHashTable *probe_hash;
521 size_t packet_length;
523 g_return_val_if_fail(probe!=NULL,FALSE);
525 probe_unique=g_random_int();
527 probe_hash=g_hash_table_new(
528 g_direct_hash, /* hash_func */
529 g_direct_equal); /* key_equal_func */
530 g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_PORT) ,GUINT_TO_POINTER(port_local));
531 g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),GUINT_TO_POINTER(probe_unique));
532 packet=packet_assembly(&packet_length,probe_hash);
533 g_hash_table_destroy(probe_hash);
537 UDPGATE_MEMZERO(&sockaddr_in_server);
538 sockaddr_in_server.sin_family=AF_INET;
539 sockaddr_in_server.sin_port=htons(PROBE_PORT);
540 sockaddr_in_server.sin_addr.s_addr=htonl(PROBE_INADDR);
541 /* FIXME: errors checking */
543 probe->gpollfd.fd, /* s */
545 packet_length, /* len */
547 (struct sockaddr *)&sockaddr_in_server, /* to */
548 sizeof(sockaddr_in_server)); /* tolen */
553 static gboolean master_start(gint port)
555 struct sockaddr_in sockaddr_in;
557 g_return_val_if_fail(port>=0,FALSE);
558 g_return_val_if_fail(master==NULL,FALSE);
560 /* Setup 'master': */
561 if (!(master=client_new()))
563 UDPGATE_MEMZERO(&sockaddr_in);
564 sockaddr_in.sin_family=AF_INET;
565 sockaddr_in.sin_port=htons(port);
566 sockaddr_in.sin_addr.s_addr=htonl(INADDR_ANY);
567 if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
568 g_warning("bind(sock,{AF_INET,INADDR_ANY:%d}): %m",(int)port);
574 static gboolean probe_start(gint port)
576 g_return_val_if_fail(port>=0,FALSE);
577 g_return_val_if_fail(probe==NULL,FALSE);
580 if (!(probe=client_new()))
582 probe_send(probe,port);
583 client_touch(probe); /* timeout */
587 gboolean network_start(gint port)
591 g_return_val_if_fail(port>=0,FALSE);
593 if ((pid_t)-1!=(daemon_pid=is_daemon_running())) {
594 g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
597 if (!master_start(port)) {
598 sock_gsource_destroy();
601 if (!probe_start(port)) {
602 sock_gsource_destroy();
605 write_daemon_running(getpid()); /* errors ignored */
606 if (network_notify_hostip)
607 (*network_notify_hostip)(0);
611 gboolean optarg_port_set_string(const gchar *port_string)
616 g_return_val_if_fail(port_string!=NULL,FALSE);
618 port_long=strtol(port_string,&endp,0);
620 g_warning(_("Invalid port specification, offending string: %s"),endp);
623 if (port_long<1 || port_long>=G_MAXINT || (endp && *endp)) {
624 g_warning(_("Invalid port integer number specification (%ld)"),port_long);
627 optarg_port=port_long;
631 gboolean network_stop(void)
636 if ((pid_t)-1==(daemon_pid=is_daemon_running())) {
637 g_warning(_("Cannot stop network daemon: Daemon is not running"));
640 if (daemon_pid==getpid()) {
641 sock_gsource_destroy();
645 kill(daemon_pid,SIGKILL);
647 write_daemon_running((pid_t)-1); /* unlink; errors ignored */
649 g_warning(udpgate_printf_alloca(_("Unable to stop the daemon at PID %d: %s"),
650 (int)daemon_pid,strerror(errno_save)));
654 if (network_notify_hostip)
655 (*network_notify_hostip)(0);
659 static GMainLoop *gmainloop;
660 static void network_detach_network_notify_hostip(guint32 hostip_guint32)
663 g_main_loop_quit(gmainloop);
666 gboolean network_detach(void)
668 pid_t daemon_pid,forked_pid;
670 if ((pid_t)-1==(daemon_pid=is_daemon_running()))
672 if (getpid()!=daemon_pid)
674 if (!optarg_no_fork) {
675 if ((pid_t)-1==(forked_pid=fork())) {
676 g_warning("fork(2): %m");
683 write_daemon_running(getpid()); /* errors ignored */
686 close(STDOUT_FILENO);
687 close(STDERR_FILENO);
692 network_notify_hostip=network_detach_network_notify_hostip;
693 gmainloop=g_main_loop_new(
695 TRUE); /* is_running; ignored */
696 g_main_loop_run(gmainloop); /* loop */
697 /* Unable to contact the server, aborting. */