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>
38 #include <glib/gutils.h>
42 #include "main.h" /* for optarg_verbose */
47 #define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN) /* |G_IO_PRI */
48 #define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS) /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
49 #define SERVER_INADDR 0xC37AD054 /* mms2.org = 195.122.208.84; 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_BASE 5
55 #define PROBE_TIMEOUT_SEC_MAX 3500
58 void (*network_notify_hostip)(guint32 hostip_guint32);
61 static G_CONST_RETURN gchar *pid_pathname(void)
63 static const gchar *static_pathname;
64 static const char *user_val;
67 user_val=g_get_user_name();
68 return pathname_find(&static_pathname,
69 G_STRINGIFY(LOCALSTATEDIR) "/run",PACKAGE ".pid",
70 (!user_val ? NULL : g_get_tmp_dir()),
71 (!user_val ? NULL : udpgate_printf_alloca(".%s-%s.pid",user_val,PACKAGE)),
78 struct sockaddr_in sockaddr_in_from;
82 static GSource *sock_gsource;
84 static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */
85 static struct client *master; /* no 'timeout' permitted */
86 static struct client *probe; /* 'timeout' permitted */
87 static guint32 probe_unique;
88 static guint probe_timeout_sec_now;
89 guint probe_timeout_sec_max=G_MAXUINT;
90 static gint port_local; /* for 'probe' resends */
92 static gboolean write_daemon_running(pid_t pid);
94 pid_t is_daemon_running(void)
97 char buf[LINE_MAX],*got;
103 if (!(f=fopen(pid_pathname(),"r")))
105 got=fgets(buf,sizeof(buf),f);
106 fclose(f); /* FIXME: error ignored */
109 write_daemon_running((pid_t)-1); /* unlink */
116 if (kill((pid_t)pid_int,0)) {
121 return (pid_t)pid_int;
124 static gboolean write_daemon_running(pid_t pid)
128 if (pid==(pid_t)-1) {
129 if (unlink(pid_pathname())) {
131 g_warning(_("Error removing PID file \"%s\": %m"),pid_pathname());
136 if (!(f=fopen(pid_pathname(),"w"))) {
137 static gboolean once=TRUE;
141 g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,pid_pathname());
145 fprintf(f,"%d\n",(int)pid); /* errors ignored */
146 fclose(f); /* FIXME: error ignored */
150 static void client_timeout_remove(struct client *client)
152 g_return_if_fail(client!=NULL);
153 g_return_if_fail(client!=master);
155 if (client->timeout_id) {
156 gboolean errgboolean;
159 g_message(_("Client fd %d removed timeout id %d"),client->gpollfd.fd,client->timeout_id);
160 errgboolean=g_source_remove(client->timeout_id);
161 g_assert(errgboolean==TRUE);
162 client->timeout_id=0;
166 static gboolean client_touch_timeout(struct client *client);
168 static void client_touch(struct client *client,guint timeout_sec)
170 g_return_if_fail(client!=NULL);
171 g_return_if_fail(client!=master);
172 g_return_if_fail(timeout_sec>0);
174 client_timeout_remove(client);
175 client->timeout_id=g_timeout_add(
176 timeout_sec*1000, /* interval; msec */
177 (GSourceFunc)client_touch_timeout, /* function */
180 g_message(_("Client fd %d new timeout id %d"),client->gpollfd.fd,client->timeout_id);
181 g_assert(client->timeout_id!=0);
184 static void client_destroy(struct client *client);
185 static gboolean probe_send(struct client *probe,gint port_local);
187 static gboolean client_touch_timeout(struct client *client)
189 g_return_val_if_fail(client!=NULL,FALSE); /* FALSE=>should be removed */
190 g_return_val_if_fail(client!=master,FALSE); /* FALSE=>should be removed */
193 g_message(_("Client fd %d timeout id %d occured/entered"),client->gpollfd.fd,client->timeout_id);
195 /* Do not destroy the timeout in client_destroy().
196 * It would crash GLib - we remove it be returning FALSE from here.
198 g_assert(client->timeout_id!=0);
199 client->timeout_id=0;
202 if (probe_timeout_sec_now<probe_timeout_sec_max) {
203 probe_timeout_sec_now*=PROBE_TIMEOUT_SEC_BASE;
204 g_assert(probe_timeout_sec_max==G_MAXUINT || probe_timeout_sec_max<PROBE_TIMEOUT_SEC_MAX);
205 probe_timeout_sec_now=MIN(probe_timeout_sec_now,probe_timeout_sec_max);
206 probe_timeout_sec_now=MIN(probe_timeout_sec_now,PROBE_TIMEOUT_SEC_MAX);
207 if (probe_send(probe,port_local)) {
208 client_touch(probe,probe_timeout_sec_now); /* timeout */
209 return FALSE; /* GSource should be removed */
211 /* failure FALLTHRU */
214 /* Never destroy 'client' now - it has been destroyed by network_stop()! */
215 g_warning(_("No probe response from the server, stopping the daemon. Please check your public Internet connectivity."));
218 client_destroy(client);
222 g_message(_("Client timeout occurance finish"));
224 return FALSE; /* GSource should be removed */
227 static void handle_master_probe(const void *packet,size_t gotlen,const struct sockaddr_in *sockaddr_in_from)
229 GHashTable *got_hash;
230 gpointer got_unique_gpointer;
231 gpointer hostip_gpointer;
232 guint32 hostip_guint32;
234 g_return_if_fail(packet!=NULL);
235 g_return_if_fail(sockaddr_in_from!=NULL);
240 if (!(got_hash=packet_disassembly(packet,gotlen)))
242 if (!(g_hash_table_lookup_extended(
243 got_hash, /* hash_table */
244 GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32), /* lookup_key */
246 &got_unique_gpointer))) {
247 err_packet_disassembly_destroy_got_hash:
248 packet_disassembly_destroy(got_hash);
251 if (GPOINTER_TO_UINT(got_unique_gpointer)!=probe_unique)
252 goto err_packet_disassembly_destroy_got_hash;
253 if (!(g_hash_table_lookup_extended(
254 got_hash, /* hash_table */
255 GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_INADDR), /* lookup_key */
258 goto err_packet_disassembly_destroy_got_hash;
259 hostip_guint32=GPOINTER_TO_UINT(hostip_gpointer);
260 packet_disassembly_destroy(got_hash);
262 client_destroy(probe);
263 if (network_notify_hostip)
264 (*network_notify_hostip)(hostip_guint32);
267 static struct client *client_new(void);
269 static void handle_master(struct client *master)
271 g_return_if_fail(master!=NULL);
274 char packet[0x10000];
276 struct sockaddr_in sockaddr_in_from;
277 struct client *client=NULL /* Prevent false positive: might be used uninitialized */;
278 struct sockaddr_in sockaddr_in_server;
279 socklen_t sockaddr_in_from_length;
282 sockaddr_in_from_length=sizeof(sockaddr_in_from);
283 if (-1==(gotlen=recvfrom(
284 master->gpollfd.fd, /* s */
286 sizeof(packet), /* len */
288 (struct sockaddr *)&sockaddr_in_from, /* from */
289 &sockaddr_in_from_length))) /* fromlen */
292 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
295 if (packet_recognized(packet,gotlen)) {
296 handle_master_probe(packet,gotlen,&sockaddr_in_from);
299 /* Not yet initialized by 'probe' reply - drop it. */
302 g_message(_("Data packet received from %s but no probe reply yet; dropping packet."),
303 SOCKADDR_IN_TO_STRING(&sockaddr_in_from));
307 /* FIXME: Performance: Ugly search... */
308 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
309 client=clientl->data;
313 && client->sockaddr_in_from.sin_family ==sockaddr_in_from.sin_family
314 && client->sockaddr_in_from.sin_port ==sockaddr_in_from.sin_port
315 && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr)
320 client->sockaddr_in_from=sockaddr_in_from;
322 client_touch(client,CLIENT_TIMEOUT_SEC);
323 UDPGATE_MEMZERO(&sockaddr_in_server);
324 sockaddr_in_server.sin_family=AF_INET;
325 sockaddr_in_server.sin_port=htons(SERVER_PORT);
326 sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR);
327 /* FIXME: errors checking */
329 client->gpollfd.fd, /* s */
333 (struct sockaddr *)&sockaddr_in_server, /* to */
334 sizeof(sockaddr_in_server)); /* tolen */
338 static void handle_probe(struct client *probe)
340 g_return_if_fail(probe!=NULL);
344 struct sockaddr_in sockaddr_in_from;
345 char packet[0x10000];
346 socklen_t sockaddr_in_from_length;
348 sockaddr_in_from_length=sizeof(sockaddr_in_from);
349 if (-1==(gotlen=recvfrom(
350 master->gpollfd.fd, /* s */
352 sizeof(packet), /* len */
354 (struct sockaddr *)&sockaddr_in_from, /* from */
355 &sockaddr_in_from_length))) /* fromlen */
357 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
360 /* Probe socket should have no response; maybe some ICMP errors - ignored. */
364 static void handle_client(struct client *client)
366 g_return_if_fail(client!=NULL);
367 g_return_if_fail(master!=NULL);
371 struct sockaddr_in sockaddr_in_from;
372 char packet [0x10000];
373 socklen_t sockaddr_in_from_length;
375 sockaddr_in_from_length=sizeof(sockaddr_in_from);
376 if (-1==(gotlen=recvfrom(
377 client->gpollfd.fd, /* s */
379 sizeof(packet), /* len */
381 (struct sockaddr *)&sockaddr_in_from, /* from */
382 &sockaddr_in_from_length))) /* fromlen */
384 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
386 client_touch(client,CLIENT_TIMEOUT_SEC);
387 /* FIXME: errors checking */
389 master->gpollfd.fd, /* s */
393 (struct sockaddr *)&client->sockaddr_in_from, /* to */
394 sizeof(client->sockaddr_in_from)); /* tolen */
398 static gboolean sock_source_callback(gpointer data /* unused */)
402 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
403 struct client *client=clientl->data;
405 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
406 /**/ if (client==master)
407 handle_master(client);
408 else if (client==probe)
409 handle_probe(client);
411 handle_client(client);
415 return TRUE; /* the source should be kept active */
418 static gboolean sock_source_prepare(GSource *source,gint *timeout)
425 static gboolean sock_source_check(GSource *source)
429 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
430 struct client *client=clientl->data;
432 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS)
438 static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
440 g_assert(callback!=NULL);
441 return (*callback)(user_data);
444 static GSourceFuncs sock_source_watch_funcs={
447 sock_source_dispatch,
451 static void sock_gsource_destroy(void)
453 while (sock_client_list)
454 client_destroy(sock_client_list->data);
456 g_assert(master==NULL);
457 g_assert(probe==NULL);
460 g_source_destroy(sock_gsource);
464 write_daemon_running((pid_t)-1); /* unlink; errors ignored */
467 static gboolean sock_gsource_new(void)
472 g_assert(sock_client_list==NULL);
474 /* attach sock_source_callback() to watch for any abnormalities
475 * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
477 if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) {
478 g_warning("g_source_new(): %m");
481 g_source_set_callback(
482 sock_gsource, /* source */
483 sock_source_callback, /* func */
486 if (!g_source_attach( /* returns 'guint' id */
487 sock_gsource, /* source */
488 NULL)) { /* context; NULL means 'default context' */
489 g_warning("g_source_attach(gsource,NULL): %m");
490 sock_gsource_destroy();
496 static struct client *client_new(void)
498 struct client *client;
499 static unsigned long oneul=1;
502 if (!sock_gsource_new())
505 if (-1==(sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))) {
506 g_warning("socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP): %m");
509 if (ioctl(sock,FIONBIO,&oneul)) { /* non-blocking mode */
510 g_warning("ioctl(sock,FIONBIO,&1): %m");
511 close(sock); /* errors ignored */
516 client->gpollfd.fd=sock;
517 client->gpollfd.events=SOCK_SOURCE_CHECK_EVENTS;
518 client->gpollfd.revents=0;
519 client->timeout_id=0;
520 sock_client_list=g_list_prepend(sock_client_list,client);
521 g_source_add_poll(sock_gsource,&client->gpollfd);
524 g_message(_("Client fd %d created"),client->gpollfd.fd);
529 static void client_destroy(struct client *client)
531 g_return_if_fail(client!=NULL);
533 if (!sock_gsource_new())
537 g_message(_("Client fd %d timeout id %d destroy enter"),client->gpollfd.fd,client->timeout_id);
539 if (client==master) {
541 g_assert(client->timeout_id==0);
546 client_timeout_remove(client);
549 g_source_remove_poll(sock_gsource,&client->gpollfd);
550 sock_client_list=g_list_remove(sock_client_list,client);
551 close(client->gpollfd.fd); /* errors ignored */
554 g_message(_("Client fd %d timeout id %d destroy finish"),client->gpollfd.fd,client->timeout_id);
559 static gboolean probe_send(struct client *probe,gint port_local)
561 struct sockaddr_in sockaddr_in_server;
562 GHashTable *probe_hash;
564 size_t packet_length;
566 g_return_val_if_fail(probe!=NULL,FALSE);
568 probe_unique=g_random_int();
570 probe_hash=g_hash_table_new(
571 g_direct_hash, /* hash_func */
572 g_direct_equal); /* key_equal_func */
573 g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_PORT) ,GUINT_TO_POINTER(port_local));
574 g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),GUINT_TO_POINTER(probe_unique));
575 packet=packet_assembly(&packet_length,probe_hash);
576 g_hash_table_destroy(probe_hash);
580 UDPGATE_MEMZERO(&sockaddr_in_server);
581 sockaddr_in_server.sin_family=AF_INET;
582 sockaddr_in_server.sin_port=htons(PROBE_PORT);
583 sockaddr_in_server.sin_addr.s_addr=htonl(PROBE_INADDR);
584 /* FIXME: errors checking */
586 probe->gpollfd.fd, /* s */
588 packet_length, /* len */
590 (struct sockaddr *)&sockaddr_in_server, /* to */
591 sizeof(sockaddr_in_server)); /* tolen */
596 static gboolean master_start(gint port)
598 struct sockaddr_in sockaddr_in;
601 g_return_val_if_fail(port>=0,FALSE);
602 g_return_val_if_fail(master==NULL,FALSE);
605 if (port < 0 || port_use != port) {
606 g_warning(_("Port value %d is not valid for IPv4!"),(int)port);
610 /* Setup 'master': */
611 if (!(master=client_new()))
613 UDPGATE_MEMZERO(&sockaddr_in);
614 sockaddr_in.sin_family=AF_INET;
615 sockaddr_in.sin_port=htons(port_use);
616 sockaddr_in.sin_addr.s_addr=htonl(INADDR_ANY);
617 if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
618 g_warning("bind(sock,{AF_INET,INADDR_ANY:%u}): %m",(unsigned)port_use);
624 static gboolean probe_start(gint port)
626 g_return_val_if_fail(port>=0,FALSE);
627 g_return_val_if_fail(probe==NULL,FALSE);
630 if (!(probe=client_new()))
633 if (!probe_send(probe,port)) {
634 client_destroy(probe);
637 probe_timeout_sec_now=PROBE_TIMEOUT_SEC_BASE;
638 client_touch(probe,probe_timeout_sec_now); /* timeout */
642 gboolean network_start(gint port)
646 g_return_val_if_fail(port>=0,FALSE);
648 if ((pid_t)-1!=(daemon_pid=is_daemon_running())) {
649 g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
652 if (!master_start(port)) {
653 sock_gsource_destroy();
656 if (!probe_start(port)) {
657 sock_gsource_destroy();
660 write_daemon_running(getpid()); /* errors ignored */
661 if (network_notify_hostip)
662 (*network_notify_hostip)(0);
666 gboolean optarg_port_set_string(const gchar *port_string)
671 g_return_val_if_fail(port_string!=NULL,FALSE);
673 port_long=strtol(port_string,&endp,0);
675 g_warning(_("Invalid port specification, offending string: %s"),endp);
678 if (port_long<1 || port_long>=G_MAXINT || (endp && *endp)) {
679 g_warning(_("Invalid port integer number specification (%ld)"),port_long);
682 optarg_port=port_long;
686 gboolean network_stop(void)
691 if ((pid_t)-1==(daemon_pid=is_daemon_running())) {
692 g_warning(_("Cannot stop network daemon: Daemon is not running"));
695 if (daemon_pid==getpid()) {
696 sock_gsource_destroy();
700 kill(daemon_pid,SIGKILL);
702 write_daemon_running((pid_t)-1); /* unlink; errors ignored */
704 g_warning(udpgate_printf_alloca(_("Unable to stop the daemon at PID %d: %s"),
705 (int)daemon_pid,strerror(errno_save)));
709 if (network_notify_hostip)
710 (*network_notify_hostip)(0);
714 static GMainLoop *gmainloop;
715 static void network_detach_network_notify_hostip(guint32 hostip_guint32)
718 g_main_loop_quit(gmainloop);
721 gboolean network_detach(void)
723 pid_t daemon_pid,forked_pid;
725 if ((pid_t)-1==(daemon_pid=is_daemon_running()))
727 if (getpid()!=daemon_pid)
729 if (!optarg_no_fork) {
730 if ((pid_t)-1==(forked_pid=fork())) {
731 g_warning("fork(2): %m");
738 write_daemon_running(getpid()); /* errors ignored */
741 close(STDOUT_FILENO);
742 close(STDERR_FILENO);
747 network_notify_hostip=network_detach_network_notify_hostip;
748 gmainloop=g_main_loop_new(
750 TRUE); /* is_running; ignored */
751 g_main_loop_run(gmainloop); /* loop */
752 /* Unable to contact the server, aborting. */