30fde922c79e9fd3bf02447565b1bc7eb867fab8
[udpgate.git] / src / network.c
1 /* $Id$
2  * UDP forwarding utility
3  * Copyright (C) 2004 Jan Kratochvil <project-udpforward@jankratochvil.net>
4  * 
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
8  * 
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.
13  * 
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
17  */
18
19
20 #include "config.h"
21
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>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <glib/giochannel.h>
33 #include <sys/ioctl.h>
34 #include <string.h>
35 #include <glib/galloca.h>
36 #include <glib/gprintf.h>
37
38 #include "network.h"
39
40
41 /* Config: */
42 #define NETWORK_PATHNAME_PID "/var/run/udpforward.pid"
43 #define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN)      /* |G_IO_PRI */
44 #define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS)    /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
45 #define SERVER_INADDR 0x511F02EA        /* paulina.vellum.cz = 81.31.2.234 */
46 #define SERVER_PORT   9201
47 #define CLIENT_TIMEOUT_SEC (5*60)
48
49
50 struct client {
51         GPollFD gpollfd;
52         struct sockaddr_in sockaddr_in_from;
53         guint timeout_id;
54         };
55
56 static GSource *sock_gsource;
57
58 static GList *sock_client_list; /* of 'struct client *' */
59 struct client *master;
60
61 static gboolean write_daemon_running(pid_t pid);
62
63 pid_t is_daemon_running(void)
64 {
65 FILE *f;
66 char buf[LINE_MAX],*got;
67 int pid_int;
68
69         if (sock_gsource)
70                 return getpid();
71
72         if (!(f=fopen(NETWORK_PATHNAME_PID,"r")))
73                 goto err;
74         got=fgets(buf,sizeof(buf),f);
75         fclose(f);      /* FIXME: error ignored */
76         if (got!=buf) {
77 err_unlink:
78                 write_daemon_running((pid_t)-1);        /* unlink */
79 err:
80                 return (pid_t)-1;
81                 }
82         pid_int=atoi(buf);
83         if (pid_int<=1)
84                 goto err_unlink;
85         if (kill((pid_t)pid_int,0)) {
86                 if (errno==ESRCH)
87                         goto err_unlink;
88                 goto err;
89                 }
90         return (pid_t)pid_int;
91 }
92
93 static gboolean write_daemon_running(pid_t pid)
94 {
95 FILE *f;
96
97         if (pid==(pid_t)-1) {
98                 if (unlink(NETWORK_PATHNAME_PID)) {
99                         if (errno!=ENOENT)
100                                 g_warning(_("Error removing PID file \"%s\": %m"),NETWORK_PATHNAME_PID);
101                         return FALSE;
102                         }
103                 return TRUE;
104                 }
105         if (!(f=fopen(NETWORK_PATHNAME_PID,"w"))) {
106                 g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,NETWORK_PATHNAME_PID);
107                 return FALSE;
108                 }
109         fprintf(f,"%d\n",(int)pid);     /* errors ignored */
110         fclose(f);      /* FIXME: error ignored */
111         return TRUE;
112 }
113
114 static void client_timeout_remove(struct client *client)
115 {
116         g_return_if_fail(client!=NULL);
117         g_return_if_fail(client!=master);
118
119         if (client->timeout_id) {
120 gboolean errgboolean;
121
122                 errgboolean=g_source_remove(client->timeout_id);
123                 g_assert(errgboolean==TRUE);
124                 client->timeout_id=0;
125                 }
126 }
127
128 static gboolean client_touch_timeout(struct client *client);
129
130 static void client_touch(struct client *client)
131 {
132         g_return_if_fail(client!=NULL);
133         g_return_if_fail(client!=master);
134
135         client_timeout_remove(client);
136         client->timeout_id=g_timeout_add(
137                         CLIENT_TIMEOUT_SEC*1000,        /* interval; msec */
138                         (GSourceFunc)client_touch_timeout,      /* function */
139                         client);        /* data */
140         g_assert(client->timeout_id!=0);
141 }
142
143 static void client_destroy(struct client *client);
144
145 static gboolean client_touch_timeout(struct client *client)
146 {
147         g_return_val_if_fail(client!=NULL,FALSE);       /* FALSE=>should be removed */
148         g_return_val_if_fail(client!=master,FALSE);     /* FALSE=>should be removed */
149
150         client_destroy(client);
151
152         return FALSE;   /* GSource should be removed */
153 }
154
155 static struct client *client_new(void);
156
157 static void handle_master(struct client *master)
158 {
159 ssize_t gotlen;
160 struct sockaddr_in sockaddr_in_from;
161 char packet[0x10000];
162 struct client *client;
163 struct sockaddr_in sockaddr_in_server;
164 socklen_t sockaddr_in_from_len;
165
166         g_return_if_fail(master!=NULL);
167
168         sockaddr_in_from_len=sizeof(sockaddr_in_from);
169         while (-1!=(gotlen=recvfrom(
170                         master->gpollfd.fd,     /* s */
171                         packet, /* buf */
172                         sizeof(packet), /* len */
173                         0,      /* flags */
174                         (struct sockaddr *)&sockaddr_in_from,   /* from */
175                         &sockaddr_in_from_len))) {      /* fromlen */
176 GList *clientl;
177
178                 if (sockaddr_in_from_len!=sizeof(sockaddr_in_from))     /* FIXME: errors reporting */
179                         continue;
180                 /* FIXME: Performance: Ugly search... */
181                 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
182                         client=clientl->data;
183                         if (client==master)
184                                 continue;
185                         if (1
186                                         && client->sockaddr_in_from.sin_family     ==sockaddr_in_from.sin_family
187                                         && client->sockaddr_in_from.sin_port       ==sockaddr_in_from.sin_port
188                                         && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr)
189                                 break;
190                         }
191                 if (!clientl) {
192                         client=client_new();
193                         client->sockaddr_in_from=sockaddr_in_from;
194                         }
195                 client_touch(client);
196                 UDPFORWARD_MEMZERO(&sockaddr_in_server);
197                 sockaddr_in_server.sin_family=AF_INET;
198                 sockaddr_in_server.sin_port=htons(SERVER_PORT);
199                 sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR);
200                 /* FIXME: errors checking */
201                 sendto(
202                                 client->gpollfd.fd,     /* s */
203                                 packet, /* msg */
204                                 gotlen, /* len */
205                                 0,      /* flags */
206                                 (struct sockaddr *)&sockaddr_in_server, /* to */
207                                 sizeof(sockaddr_in_server));    /* tolen */
208                 }
209 }
210
211 static void handle_client(struct client *client)
212 {
213 ssize_t gotlen;
214 struct sockaddr_in sockaddr_in_from;
215 char packet [0x10000];
216 socklen_t sockaddr_in_from_len;
217
218         g_return_if_fail(client!=NULL);
219         g_return_if_fail(master!=NULL);
220
221         while (-1!=(gotlen=recvfrom(
222                         client->gpollfd.fd,     /* s */
223                         packet, /* buf */
224                         sizeof(packet), /* len */
225                         0,      /* flags */
226                         (struct sockaddr *)&sockaddr_in_from,   /* from */
227                         &sockaddr_in_from_len))) {      /* fromlen */
228                 if (sockaddr_in_from_len!=sizeof(sockaddr_in_from))     /* FIXME: errors reporting */
229                         continue;
230                 client_touch(client);
231                 /* FIXME: errors checking */
232                 sendto(
233                                 master->gpollfd.fd,     /* s */
234                                 packet, /* msg */
235                                 gotlen, /* len */
236                                 0,      /* flags */
237                                 (struct sockaddr *)&client->sockaddr_in_from,   /* to */
238                                 sizeof(client->sockaddr_in_from));      /* tolen */
239                 }
240 }
241
242 static gboolean sock_source_callback(gpointer data /* unused */)
243 {
244 GList *clientl;
245
246         for (clientl=sock_client_list;clientl;clientl=clientl->next) {
247 struct client *client=clientl->data;
248
249                 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
250                         if (client==master)
251                                 handle_master(client);
252                         else
253                                 handle_client(client);
254                         }
255                 }
256
257         return TRUE; /* the source should be kept active */
258 }
259
260 static gboolean sock_source_prepare(GSource *source,gint *timeout)
261 {
262         *timeout=-1;
263
264         return FALSE;
265 }
266
267 static gboolean sock_source_check(GSource *source)
268 {
269 GList *clientl;
270
271         for (clientl=sock_client_list;clientl;clientl=clientl->next) {
272 struct client *client=clientl->data;
273
274                 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS)
275                         return TRUE;
276                 }
277         return FALSE;
278 }
279
280 static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
281 {
282         g_assert(callback!=NULL);
283         return (*callback)(user_data);
284 }
285
286 static GSourceFuncs sock_source_watch_funcs={
287                 sock_source_prepare,
288                 sock_source_check,
289                 sock_source_dispatch,
290                 NULL, /* finalize */
291                 };
292
293 static void sock_gsource_destroy(void)
294 {
295         while (sock_client_list)
296                 client_destroy(sock_client_list->data);
297
298         g_assert(master==NULL);
299
300         if (sock_gsource) {
301                 g_source_destroy(sock_gsource);
302                 sock_gsource=NULL;
303                 }
304
305         write_daemon_running((pid_t)-1);        /* unlink */
306 }
307
308 static gboolean sock_gsource_new(void)
309 {
310         if (sock_gsource)
311                 return TRUE;
312
313         g_assert(sock_client_list==NULL);
314
315         /* attach sock_source_callback() to watch for any abnormalities
316          * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
317          */
318         if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) {
319                 g_warning("g_source_new(): %m");
320                 return FALSE;
321                 }
322         g_source_set_callback(
323                         sock_gsource,  /* source */
324                         sock_source_callback,  /* func */
325                         NULL, /* data */
326                         NULL);  /* notify */
327         if (!g_source_attach(   /* returns 'guint' id */
328                         sock_gsource,  /* source */
329                         NULL)) {        /* context; NULL means 'default context' */
330                 g_warning("g_source_attach(gsource,NULL): %m");
331                 sock_gsource_destroy();
332                 return FALSE;
333                 }
334         return TRUE;
335 }
336
337 static struct client *client_new(void)
338 {
339 struct client *client;
340 static unsigned long oneul=1;
341 int sock;
342
343         if (!sock_gsource_new())
344                 return FALSE;
345
346         if (-1==(sock=socket(AF_INET,SOCK_DGRAM,0))) {
347                 g_warning("socket(AF_INET,SOCK_DGRAM): %m");
348                 return NULL;
349                 }
350         if (ioctl(sock,FIONBIO,&oneul)) {       /* non-blocking mode */
351                 g_warning("ioctl(sock,FIONBIO,&1): %m");
352                 close(sock);    /* errors ignored */
353                 return NULL;
354                 }
355
356         udpforward_new(client);
357         client->gpollfd.fd=sock;
358         client->gpollfd.events=SOCK_SOURCE_CHECK_EVENTS;
359         client->gpollfd.revents=0;
360         client->timeout_id=0;
361         sock_client_list=g_list_prepend(sock_client_list,client);
362         g_source_add_poll(sock_gsource,&client->gpollfd);
363
364         return client;
365 }
366
367 static void client_destroy(struct client *client)
368 {
369         g_return_if_fail(client!=NULL);
370
371         if (!sock_gsource_new())
372                 return;
373
374         if (client==master) {
375                 master=NULL;
376                 g_assert(client->timeout_id==0);
377                 }
378         else
379                 client_timeout_remove(client);
380
381         g_source_remove_poll(sock_gsource,&client->gpollfd);
382         sock_client_list=g_list_remove(sock_client_list,client);
383         close(client->gpollfd.fd);      /* errors ignored */
384         g_free(client);
385 }
386
387 gboolean network_start(gint port)
388 {
389 pid_t daemon_pid;
390 struct sockaddr_in sockaddr_in;
391
392         g_return_val_if_fail(port>=0,FALSE);
393
394         if ((pid_t)-1!=(daemon_pid=is_daemon_running())) {
395                 g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
396                 return FALSE;
397                 }
398         g_assert(master==NULL);
399         if (!(master=client_new()))
400                 return FALSE;
401         UDPFORWARD_MEMZERO(&sockaddr_in);
402         sockaddr_in.sin_family=AF_INET;
403         sockaddr_in.sin_port=htons(port);
404         sockaddr_in.sin_addr.s_addr=INADDR_ANY;
405         if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
406                 g_warning("bind(sock,{AF_INET,INADDR_ANY:%d}): %m",(int)port);
407                 sock_gsource_destroy();
408                 return FALSE;
409                 }
410         write_daemon_running(getpid());
411         return TRUE;
412 }
413
414 gboolean network_stop(void)
415 {
416 pid_t daemon_pid;
417 int errno_save;
418
419         if ((pid_t)-1==(daemon_pid=is_daemon_running())) {
420                 g_warning(_("Cannot stop network daemon: Daemon is not running"));
421                 return FALSE;
422                 }
423         if (daemon_pid==getpid()) {
424                 sock_gsource_destroy();
425                 return TRUE;
426                 }
427         errno=0;
428         kill(daemon_pid,SIGKILL);
429         errno_save=errno;
430         if (errno_save)  {
431                 g_warning(udpforward_printf_alloca(_("Unable to stop the daemon at PID %d: %s"),
432                                 (int)daemon_pid,strerror(errno_save)));
433                 return FALSE;
434                 }
435         return TRUE;
436 }