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 */
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 0xC37AD054 /* mms2.org = 195.122.208.84; host order */
49 #define SERVER_PORT 9201 /* host order */
50 #define PROBE_INADDR SERVER_INADDR /* host order */
51 #define PROBE_PORT 8201 /* host order */
52 #define CLIENT_TIMEOUT_SEC (5*60)
53 #define PROBE_TIMEOUT_SEC_BASE 5
54 #define PROBE_TIMEOUT_SEC_MAX 3500
57 void (*network_notify_hostip)(guint32 hostip_guint32);
60 static G_CONST_RETURN gchar *pid_pathname(void)
62 static const gchar *static_pathname;
63 static const char *user_val;
66 user_val=getenv("USER");
67 return pathname_find(&static_pathname,
68 G_STRINGIFY(LOCALSTATEDIR) "/run",PACKAGE ".pid",
69 (!user_val ? NULL : "/tmp"),
70 (!user_val ? NULL : udpgate_printf_alloca(".%s-%s.pid",user_val,PACKAGE)),
77 struct sockaddr_in sockaddr_in_from;
81 static GSource *sock_gsource;
83 static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */
84 static struct client *master; /* no 'timeout' permitted */
85 static struct client *probe; /* 'timeout' permitted */
86 static guint32 probe_unique;
87 static guint probe_timeout_sec_now;
88 guint probe_timeout_sec_max=G_MAXUINT;
89 static gint port_local; /* for 'probe' resends */
91 static gboolean write_daemon_running(pid_t pid);
93 pid_t is_daemon_running(void)
96 char buf[LINE_MAX],*got;
102 if (!(f=fopen(pid_pathname(),"r")))
104 got=fgets(buf,sizeof(buf),f);
105 fclose(f); /* FIXME: error ignored */
108 write_daemon_running((pid_t)-1); /* unlink */
115 if (kill((pid_t)pid_int,0)) {
120 return (pid_t)pid_int;
123 static gboolean write_daemon_running(pid_t pid)
127 if (pid==(pid_t)-1) {
128 if (unlink(pid_pathname())) {
130 g_warning(_("Error removing PID file \"%s\": %m"),pid_pathname());
135 if (!(f=fopen(pid_pathname(),"w"))) {
136 static gboolean once=TRUE;
140 g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,pid_pathname());
144 fprintf(f,"%d\n",(int)pid); /* errors ignored */
145 fclose(f); /* FIXME: error ignored */
149 static void client_timeout_remove(struct client *client)
151 g_return_if_fail(client!=NULL);
152 g_return_if_fail(client!=master);
154 if (client->timeout_id) {
155 gboolean errgboolean;
158 g_message(_("Client fd %d removed timeout id %d"),client->gpollfd.fd,client->timeout_id);
159 errgboolean=g_source_remove(client->timeout_id);
160 g_assert(errgboolean==TRUE);
161 client->timeout_id=0;
165 static gboolean client_touch_timeout(struct client *client);
167 static void client_touch(struct client *client,guint timeout_sec)
169 g_return_if_fail(client!=NULL);
170 g_return_if_fail(client!=master);
171 g_return_if_fail(timeout_sec>0);
173 client_timeout_remove(client);
174 client->timeout_id=g_timeout_add(
175 timeout_sec*1000, /* interval; msec */
176 (GSourceFunc)client_touch_timeout, /* function */
179 g_message(_("Client fd %d new timeout id %d"),client->gpollfd.fd,client->timeout_id);
180 g_assert(client->timeout_id!=0);
183 static void client_destroy(struct client *client);
184 static gboolean probe_send(struct client *probe,gint port_local);
186 static gboolean client_touch_timeout(struct client *client)
188 g_return_val_if_fail(client!=NULL,FALSE); /* FALSE=>should be removed */
189 g_return_val_if_fail(client!=master,FALSE); /* FALSE=>should be removed */
192 g_message(_("Client fd %d timeout id %d occured/entered"),client->gpollfd.fd,client->timeout_id);
194 /* Do not destroy the timeout in client_destroy().
195 * It would crash GLib - we remove it be returning FALSE from here.
197 g_assert(client->timeout_id!=0);
198 client->timeout_id=0;
201 if (probe_timeout_sec_now<probe_timeout_sec_max) {
202 probe_timeout_sec_now*=PROBE_TIMEOUT_SEC_BASE;
203 g_assert(probe_timeout_sec_max==G_MAXUINT || probe_timeout_sec_max<PROBE_TIMEOUT_SEC_MAX);
204 probe_timeout_sec_now=MIN(probe_timeout_sec_now,probe_timeout_sec_max);
205 probe_timeout_sec_now=MIN(probe_timeout_sec_now,PROBE_TIMEOUT_SEC_MAX);
206 if (probe_send(probe,port_local)) {
207 client_touch(probe,probe_timeout_sec_now); /* timeout */
208 return FALSE; /* GSource should be removed */
210 /* failure FALLTHRU */
213 /* Never destroy 'client' now - it has been destroyed by network_stop()! */
214 g_warning(_("No probe response from the server, stopping the daemon. Please check your public Internet connectivity."));
217 client_destroy(client);
221 g_message(_("Client timeout occurance finish"));
223 return FALSE; /* GSource should be removed */
226 static void handle_master_probe(const void *packet,size_t gotlen,const struct sockaddr_in *sockaddr_in_from)
228 GHashTable *got_hash;
229 gpointer got_unique_gpointer;
230 gpointer hostip_gpointer;
231 guint32 hostip_guint32;
233 g_return_if_fail(packet!=NULL);
234 g_return_if_fail(sockaddr_in_from!=NULL);
239 if (!(got_hash=packet_disassembly(packet,gotlen)))
241 if (!(g_hash_table_lookup_extended(
242 got_hash, /* hash_table */
243 GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32), /* lookup_key */
245 &got_unique_gpointer))) {
246 err_packet_disassembly_destroy_got_hash:
247 packet_disassembly_destroy(got_hash);
250 if (GPOINTER_TO_UINT(got_unique_gpointer)!=probe_unique)
251 goto err_packet_disassembly_destroy_got_hash;
252 if (!(g_hash_table_lookup_extended(
253 got_hash, /* hash_table */
254 GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_INADDR), /* lookup_key */
257 goto err_packet_disassembly_destroy_got_hash;
258 hostip_guint32=GPOINTER_TO_UINT(hostip_gpointer);
259 packet_disassembly_destroy(got_hash);
261 client_destroy(probe);
262 if (network_notify_hostip)
263 (*network_notify_hostip)(hostip_guint32);
266 static struct client *client_new(void);
268 static void handle_master(struct client *master)
270 g_return_if_fail(master!=NULL);
273 char packet[0x10000];
275 struct sockaddr_in sockaddr_in_from;
276 struct client *client=NULL /* Prevent false positive: might be used uninitialized */;
277 struct sockaddr_in sockaddr_in_server;
278 socklen_t sockaddr_in_from_length;
281 sockaddr_in_from_length=sizeof(sockaddr_in_from);
282 if (-1==(gotlen=recvfrom(
283 master->gpollfd.fd, /* s */
285 sizeof(packet), /* len */
287 (struct sockaddr *)&sockaddr_in_from, /* from */
288 &sockaddr_in_from_length))) /* fromlen */
291 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
294 if (packet_recognized(packet,gotlen)) {
295 handle_master_probe(packet,gotlen,&sockaddr_in_from);
298 /* Not yet initialized by 'probe' reply - drop it. */
301 g_message(_("Data packet received from %s but no probe reply yet; dropping packet."),
302 SOCKADDR_IN_TO_STRING(&sockaddr_in_from));
306 /* FIXME: Performance: Ugly search... */
307 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
308 client=clientl->data;
312 && client->sockaddr_in_from.sin_family ==sockaddr_in_from.sin_family
313 && client->sockaddr_in_from.sin_port ==sockaddr_in_from.sin_port
314 && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr)
319 client->sockaddr_in_from=sockaddr_in_from;
321 client_touch(client,CLIENT_TIMEOUT_SEC);
322 UDPGATE_MEMZERO(&sockaddr_in_server);
323 sockaddr_in_server.sin_family=AF_INET;
324 sockaddr_in_server.sin_port=htons(SERVER_PORT);
325 sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR);
326 /* FIXME: errors checking */
328 client->gpollfd.fd, /* s */
332 (struct sockaddr *)&sockaddr_in_server, /* to */
333 sizeof(sockaddr_in_server)); /* tolen */
337 static void handle_probe(struct client *probe)
339 g_return_if_fail(probe!=NULL);
343 struct sockaddr_in sockaddr_in_from;
344 char packet[0x10000];
345 socklen_t sockaddr_in_from_length;
347 sockaddr_in_from_length=sizeof(sockaddr_in_from);
348 if (-1==(gotlen=recvfrom(
349 master->gpollfd.fd, /* s */
351 sizeof(packet), /* len */
353 (struct sockaddr *)&sockaddr_in_from, /* from */
354 &sockaddr_in_from_length))) /* fromlen */
356 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
359 /* Probe socket should have no response; maybe some ICMP errors - ignored. */
363 static void handle_client(struct client *client)
365 g_return_if_fail(client!=NULL);
366 g_return_if_fail(master!=NULL);
370 struct sockaddr_in sockaddr_in_from;
371 char packet [0x10000];
372 socklen_t sockaddr_in_from_length;
374 sockaddr_in_from_length=sizeof(sockaddr_in_from);
375 if (-1==(gotlen=recvfrom(
376 client->gpollfd.fd, /* s */
378 sizeof(packet), /* len */
380 (struct sockaddr *)&sockaddr_in_from, /* from */
381 &sockaddr_in_from_length))) /* fromlen */
383 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
385 client_touch(client,CLIENT_TIMEOUT_SEC);
386 /* FIXME: errors checking */
388 master->gpollfd.fd, /* s */
392 (struct sockaddr *)&client->sockaddr_in_from, /* to */
393 sizeof(client->sockaddr_in_from)); /* tolen */
397 static gboolean sock_source_callback(gpointer data /* unused */)
401 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
402 struct client *client=clientl->data;
404 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
405 /**/ if (client==master)
406 handle_master(client);
407 else if (client==probe)
408 handle_probe(client);
410 handle_client(client);
414 return TRUE; /* the source should be kept active */
417 static gboolean sock_source_prepare(GSource *source,gint *timeout)
424 static gboolean sock_source_check(GSource *source)
428 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
429 struct client *client=clientl->data;
431 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS)
437 static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
439 g_assert(callback!=NULL);
440 return (*callback)(user_data);
443 static GSourceFuncs sock_source_watch_funcs={
446 sock_source_dispatch,
450 static void sock_gsource_destroy(void)
452 while (sock_client_list)
453 client_destroy(sock_client_list->data);
455 g_assert(master==NULL);
456 g_assert(probe==NULL);
459 g_source_destroy(sock_gsource);
463 write_daemon_running((pid_t)-1); /* unlink; errors ignored */
466 static gboolean sock_gsource_new(void)
471 g_assert(sock_client_list==NULL);
473 /* attach sock_source_callback() to watch for any abnormalities
474 * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
476 if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) {
477 g_warning("g_source_new(): %m");
480 g_source_set_callback(
481 sock_gsource, /* source */
482 sock_source_callback, /* func */
485 if (!g_source_attach( /* returns 'guint' id */
486 sock_gsource, /* source */
487 NULL)) { /* context; NULL means 'default context' */
488 g_warning("g_source_attach(gsource,NULL): %m");
489 sock_gsource_destroy();
495 static struct client *client_new(void)
497 struct client *client;
498 static unsigned long oneul=1;
501 if (!sock_gsource_new())
504 if (-1==(sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))) {
505 g_warning("socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP): %m");
508 if (ioctl(sock,FIONBIO,&oneul)) { /* non-blocking mode */
509 g_warning("ioctl(sock,FIONBIO,&1): %m");
510 close(sock); /* errors ignored */
515 client->gpollfd.fd=sock;
516 client->gpollfd.events=SOCK_SOURCE_CHECK_EVENTS;
517 client->gpollfd.revents=0;
518 client->timeout_id=0;
519 sock_client_list=g_list_prepend(sock_client_list,client);
520 g_source_add_poll(sock_gsource,&client->gpollfd);
523 g_message(_("Client fd %d created"),client->gpollfd.fd);
528 static void client_destroy(struct client *client)
530 g_return_if_fail(client!=NULL);
532 if (!sock_gsource_new())
536 g_message(_("Client fd %d timeout id %d destroy enter"),client->gpollfd.fd,client->timeout_id);
538 if (client==master) {
540 g_assert(client->timeout_id==0);
545 client_timeout_remove(client);
548 g_source_remove_poll(sock_gsource,&client->gpollfd);
549 sock_client_list=g_list_remove(sock_client_list,client);
550 close(client->gpollfd.fd); /* errors ignored */
553 g_message(_("Client fd %d timeout id %d destroy finish"),client->gpollfd.fd,client->timeout_id);
558 static gboolean probe_send(struct client *probe,gint port_local)
560 struct sockaddr_in sockaddr_in_server;
561 GHashTable *probe_hash;
563 size_t packet_length;
565 g_return_val_if_fail(probe!=NULL,FALSE);
567 probe_unique=g_random_int();
569 probe_hash=g_hash_table_new(
570 g_direct_hash, /* hash_func */
571 g_direct_equal); /* key_equal_func */
572 g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_PORT) ,GUINT_TO_POINTER(port_local));
573 g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),GUINT_TO_POINTER(probe_unique));
574 packet=packet_assembly(&packet_length,probe_hash);
575 g_hash_table_destroy(probe_hash);
579 UDPGATE_MEMZERO(&sockaddr_in_server);
580 sockaddr_in_server.sin_family=AF_INET;
581 sockaddr_in_server.sin_port=htons(PROBE_PORT);
582 sockaddr_in_server.sin_addr.s_addr=htonl(PROBE_INADDR);
583 /* FIXME: errors checking */
585 probe->gpollfd.fd, /* s */
587 packet_length, /* len */
589 (struct sockaddr *)&sockaddr_in_server, /* to */
590 sizeof(sockaddr_in_server)); /* tolen */
595 static gboolean master_start(gint port)
597 struct sockaddr_in sockaddr_in;
600 g_return_val_if_fail(port>=0,FALSE);
601 g_return_val_if_fail(master==NULL,FALSE);
604 if (port < 0 || port_use != port) {
605 g_warning(_("Port value %d is not valid for IPv4!"),(int)port);
609 /* Setup 'master': */
610 if (!(master=client_new()))
612 UDPGATE_MEMZERO(&sockaddr_in);
613 sockaddr_in.sin_family=AF_INET;
614 sockaddr_in.sin_port=htons(port_use);
615 sockaddr_in.sin_addr.s_addr=htonl(INADDR_ANY);
616 if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
617 g_warning("bind(sock,{AF_INET,INADDR_ANY:%u}): %m",(unsigned)port_use);
623 static gboolean probe_start(gint port)
625 g_return_val_if_fail(port>=0,FALSE);
626 g_return_val_if_fail(probe==NULL,FALSE);
629 if (!(probe=client_new()))
632 if (!probe_send(probe,port)) {
633 client_destroy(probe);
636 probe_timeout_sec_now=PROBE_TIMEOUT_SEC_BASE;
637 client_touch(probe,probe_timeout_sec_now); /* timeout */
641 gboolean network_start(gint port)
645 g_return_val_if_fail(port>=0,FALSE);
647 if ((pid_t)-1!=(daemon_pid=is_daemon_running())) {
648 g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
651 if (!master_start(port)) {
652 sock_gsource_destroy();
655 if (!probe_start(port)) {
656 sock_gsource_destroy();
659 write_daemon_running(getpid()); /* errors ignored */
660 if (network_notify_hostip)
661 (*network_notify_hostip)(0);
665 gboolean optarg_port_set_string(const gchar *port_string)
670 g_return_val_if_fail(port_string!=NULL,FALSE);
672 port_long=strtol(port_string,&endp,0);
674 g_warning(_("Invalid port specification, offending string: %s"),endp);
677 if (port_long<1 || port_long>=G_MAXINT || (endp && *endp)) {
678 g_warning(_("Invalid port integer number specification (%ld)"),port_long);
681 optarg_port=port_long;
685 gboolean network_stop(void)
690 if ((pid_t)-1==(daemon_pid=is_daemon_running())) {
691 g_warning(_("Cannot stop network daemon: Daemon is not running"));
694 if (daemon_pid==getpid()) {
695 sock_gsource_destroy();
699 kill(daemon_pid,SIGKILL);
701 write_daemon_running((pid_t)-1); /* unlink; errors ignored */
703 g_warning(udpgate_printf_alloca(_("Unable to stop the daemon at PID %d: %s"),
704 (int)daemon_pid,strerror(errno_save)));
708 if (network_notify_hostip)
709 (*network_notify_hostip)(0);
713 static GMainLoop *gmainloop;
714 static void network_detach_network_notify_hostip(guint32 hostip_guint32)
717 g_main_loop_quit(gmainloop);
720 gboolean network_detach(void)
722 pid_t daemon_pid,forked_pid;
724 if ((pid_t)-1==(daemon_pid=is_daemon_running()))
726 if (getpid()!=daemon_pid)
728 if (!optarg_no_fork) {
729 if ((pid_t)-1==(forked_pid=fork())) {
730 g_warning("fork(2): %m");
737 write_daemon_running(getpid()); /* errors ignored */
740 close(STDOUT_FILENO);
741 close(STDERR_FILENO);
746 network_notify_hostip=network_detach_network_notify_hostip;
747 gmainloop=g_main_loop_new(
749 TRUE); /* is_running; ignored */
750 g_main_loop_run(gmainloop); /* loop */
751 /* Unable to contact the server, aborting. */