fc2c4fdbd409ed61c69b44497250a78c865f9994
[riece] / dcc / dcc.c
1 /* dcc.c
2  * Copyright (C) 1998-2003  Daiki Ueno
3  *
4  * This file is part of Riece.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  */
20
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #include <sys/socket.h>
24 #include <sys/file.h>
25 #include <sys/ioctl.h>
26 #include <sys/stat.h>
27 #include <sys/time.h>
28 #include <netdb.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 #include <netinet/in.h>
34 #define _GNU_SOURCE
35 #include <getopt.h>
36
37 #ifndef MAXHOSTNAMELEN
38 # define MAXHOSTNAMELEN 31
39 #endif
40
41 #ifdef HAVE_SYS_SELECT_H
42 # include <sys/select.h>
43 #endif
44
45 #ifdef HAVE_MEMMOVE
46 # ifdef HAVE_LIBGEN_H
47 #  include <libgen.h>
48 #  ifdef basename
49 #   undef basename
50 #  endif
51 # endif
52 # include <string.h>
53 #else
54 # define memmove(x,y,z) bcopy((y), (x), (z))
55 #endif
56
57 #ifndef HAVE_BASENAME
58 # define basename(path) (rindex((path), '/') + 1)
59 #endif
60
61 static void usage();
62 static int prepare_listen_port();
63 static int prepare_connect_port();
64
65 static int receive_file();
66 static int send_file();
67 static int select_loop();
68 static int chat_listen();
69 static int chat_connect();
70
71 static u_long primary_address_of();
72 static u_long extract_addr_of_string();
73 static u_long get_address_externally();
74
75 static char *progname;
76
77 void version () {
78         fprintf(stderr,
79                                         "%s (%s) %s\n"
80                                         "Copyright (C) 1998-2003 Daiki Ueno\n"
81                                         "This is free software; see the source for copying conditions.  There is NO\n"
82                                         "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", 
83                                         progname, PACKAGE, VERSION);
84 }
85
86 void usage () {
87   fprintf(stderr,
88                                         "Usage: %s [global-options] command [arguments...]\n"
89                                         "where global-options are -v, -h, etc.\n"
90                                         "where command is one of send, receive, chat, resolve.\n"
91                                         "where arguments depend on the specific command.\n\n"
92                                         "send <port> <filename>\n"
93                                         "receive <host> <port> <size> <filename>\n"
94                                         "chat listen <port>\n"
95                                         "chat connect <host> <port>\n"
96                                         "resolve [hosts ...]\n",
97                                         progname);
98 }
99
100 int prepare_listen_port (int ip_port) {
101   int sock, tries;
102   int opt = 1;
103   static struct sockaddr_in server;
104
105   
106   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
107     perror("opening stream socket");
108     exit(1);
109   }
110
111 #ifdef SO_REUSEADDR
112   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 
113                  (char *)&opt, sizeof (opt)) < 0) {
114     perror ("setsockopt SO_REUSEADDR");
115   }
116 #endif
117
118   /* Bind a port to listen for new connections */
119
120   server.sin_family = AF_INET;
121   server.sin_addr.s_addr = INADDR_ANY;
122   server.sin_port = htons (ip_port);
123   for (tries = 0; tries < 10; tries++) {
124     if (bind (sock, (struct sockaddr *) &server, sizeof (server))) {
125       if (tries >= 9) {
126         perror ("binding stream socket");
127         exit (1);
128       }
129       perror ("binding stream socket. retry in 20 seconds");
130       sleep (20);               /* wait 20 seconds and try again */
131     } else
132       break;
133   }
134   listen (sock, 64);
135   return (sock);
136 }
137
138 u_long get_address_externally(char *ircserver) {
139   int i, len, dummy;
140   u_long addr;
141   struct hostent *hp;
142   struct sockaddr_in server, client;
143
144   addr = 0xc6290004;                          /* dummy addr --- rootA */
145   if (ircserver && (hp = gethostbyname(ircserver)) != NULL) {
146     addr = ntohl(((struct in_addr *)hp->h_addr_list[0])->s_addr);
147   }
148   if ((dummy = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
149     perror("opening stream socket");
150     return -1;
151   }
152   server.sin_family = AF_INET;
153   server.sin_addr.s_addr = htonl(addr);
154   server.sin_port = htons(7);                 /* dummy port --- echo */
155   for (i = 0; i < 8; i++) {
156     server.sin_zero[i] = 0;
157   }
158   if (connect(dummy, (struct sockaddr *)&server, sizeof(server)) < 0) {
159     perror ("connecting remote socket");
160     return -1;
161   }
162   len = sizeof(client);
163   if (getsockname(dummy, (struct sockaddr *)&client, &len) < 0) 
164     return -1;
165   close(dummy);
166   return ntohl(client.sin_addr.s_addr);
167 }
168
169 \f
170 /*
171  * send_file(int port, char *ifile)
172  * listens to connections to port, and when connection established
173  * sends ifile to that socket
174  */
175 int send_file (int port, char *ifile) {
176   int sock, ifd, ofd, len;
177   u_long addr, bytessent = 0;
178   char buf[ BUFSIZ * 8 ];
179   fd_set readfds, writefds, fdset;
180   struct stat statbuf;
181   char namebuf[ MAXHOSTNAMELEN ];
182   struct hostent *hp;
183   struct sockaddr_in sin;
184
185   if ((ifd = open (ifile, O_RDONLY)) < 0) { 
186     /* error in opening file to send */
187     close(ofd);
188     return 1;
189   }
190
191   gethostname(namebuf, sizeof (namebuf));
192   fstat (ifd, &statbuf);
193
194   sock = prepare_listen_port(port);
195   len = sizeof (struct sockaddr_in);
196   if (getsockname(sock, (struct sockaddr *)&sin, &len) == 0) 
197     port = ntohs(sin.sin_port);
198  
199         if ((addr = get_address_externally (NULL)) < 0) {
200                 gethostname(namebuf, sizeof (namebuf));
201                 if (hp = gethostbyname(namebuf)) 
202                         addr = ((struct in_addr *) (hp->h_addr_list)[0])->s_addr;
203                 else
204                         return 2;
205         }
206
207         printf ("DCC send %s %d %u %d\n", ifile, port, addr, statbuf.st_size);
208   
209   ofd = accept(sock, (struct sockaddr *) 0, (int *) 0);
210   
211   while ((len = read (ifd, buf, sizeof (buf))) > 0) {
212     write (ofd, buf, len);      
213     bytessent += len;
214     while ((len = read (ofd, buf, sizeof (u_long))) &&
215            ntohl (*(u_long *) buf) != bytessent);
216   }
217   close (ofd);
218   close (ifd);
219   printf ("*** DCC file %s sent\n", ifile);
220
221   return 0;
222 }
223
224 /*
225  * receive_file(u_long host, int port, char *ifile)
226  * connects to (host,port) and reads everything send from there
227  * for every packet received gives back how much actually got
228  * puts everything in ifile
229  */
230 int receive_file (u_long host, int port, int size, char *ifile) {
231   int sock, ifd, ofd, len, bytesreceived = 0, toread, prev = 0;
232   char buf[ BUFSIZ * 8 ];
233   fd_set readfds, writefds, fdset;
234   u_long netsize;
235     
236   if ((ofd = open(ifile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
237     fprintf(stderr, "open: opening file: %s\n", ifile);
238     return 1;
239   }
240   ifd = prepare_connect_port (host, port);
241   if ((toread = sizeof (buf)) > size)
242     toread = size;
243   while (bytesreceived < size && (len = read (ifd, buf, toread)) > 0) {
244     write (ofd, buf, len);
245     bytesreceived += len;
246     netsize = htonl (bytesreceived);
247     lseek (ifd, 0, 2);
248     write (ifd, &netsize, 4);
249     lseek (ifd, 0, 2);
250     if (toread > size - bytesreceived)
251       toread = size - bytesreceived;
252     if (bytesreceived - prev > size / 5) {
253       printf ("DCC %s %d%% (%d/%d bytes) received\n", ifile,
254               100 * bytesreceived / size, bytesreceived, size);
255       prev = bytesreceived;
256     }
257   }
258   printf ("*** DCC file %s received\n", ifile);
259   close (ifd);
260   close (ofd);
261
262   return 0;
263 }
264
265 /*
266  * select_loop(int sfd)
267  * listens fd given, reads stdin and sends it to socket 
268  * anything read from socket is send to stdout
269  */
270 int select_loop (int sfd) {
271   int ofd, len, bytesreceived = 0;
272   char buf[ BUFSIZ * 8 ];
273   fd_set readfds, writefds, fdset;
274
275   for (;;) {
276     FD_ZERO (&readfds);
277     FD_SET (sfd, &readfds);
278     FD_SET (0, &readfds);
279     if (select (32, &readfds, 0, 0, 0) < 0) {
280       perror ("select");
281       close (sfd);
282       return 1;
283     }
284         
285     if (FD_ISSET (sfd, &readfds)) {
286       if ((len = read(sfd, buf, sizeof (buf))) == 0) {
287         close (sfd);
288         return 0;
289       }
290       write (1, buf, len);
291       FD_CLR (sfd, &readfds);
292     }
293     if (FD_ISSET (0, &readfds)) {
294       if ((len = read (0, buf, sizeof (buf))) == 0) {
295         close (sfd);
296         return 0;
297       }
298       write(sfd, buf, len);
299       FD_CLR (ofd, &readfds);
300     }
301   }
302 }
303
304 int prepare_connect_port (u_long host, int port) {
305   int sock;
306   static struct hostent *hp;
307   static struct sockaddr_in server;
308     
309   sock = socket (AF_INET, SOCK_STREAM, 0);
310   if (sock < 0) {
311     perror ("opening stream socket");
312     exit (1);
313   }
314   server.sin_family = AF_INET;
315   
316   server.sin_addr.s_addr = ntohl (host);
317   server.sin_port = htons (port);
318     
319   if (connect(sock, (struct sockaddr *) &server, sizeof (server)) < 0) {
320     perror ("connecting remote socket");
321     return 0;
322   }
323   
324   return sock;
325 }
326
327 u_long extract_addr_of_string (char *str) {
328   u_long result = 0;
329
330 #ifndef HAVE_STRTOUL
331   while (*str++) 
332     result = result * 10 + *str - '0';
333 #else /* !HAVE_STRTOUL */
334   result = strtoul(str, NULL, 10);
335 #endif /* HAVE_STRTOUL */
336   return result;
337 }
338
339 u_long primary_address_of (char *host) {   
340   struct hostent *hp;
341   u_long addr;
342   
343   if ((hp = gethostbyname(host)) == NULL)
344     addr = inet_addr(host);
345   else
346     memmove(&addr, hp->h_addr_list[ 0 ], 4);
347   
348   return ntohl(addr);
349 }
350
351 int chat_listen(int port) {
352   struct sockaddr_in sin;
353   struct hostent *hp;
354         u_long addr;
355   int sock, len;
356   char namebuf[ MAXHOSTNAMELEN ];
357     
358   sock = prepare_listen_port (port);
359   
360   len = sizeof (struct sockaddr_in);
361   if (getsockname(sock, (struct sockaddr *)&sin, &len) == 0) 
362     port = ntohs(sin.sin_port);
363
364         if ((addr = get_address_externally (NULL)) < 0) {
365                 gethostname(namebuf, sizeof (namebuf));
366                 if (hp = gethostbyname(namebuf)) 
367                         addr = ((struct in_addr *) (hp->h_addr_list)[0])->s_addr;
368                 else
369                         return 2;
370         }
371
372         printf("DCC chat %u %d\n", addr, port);
373   
374   if ((sock = accept(sock, (struct sockaddr *) 0, (int *) 0)) > -1) {
375                 printf("DCC chat established\n");
376     return select_loop(sock);
377         }
378   
379   return 1;
380 }
381
382 int chat_connect(u_long host, int port) {
383   int sock;
384   
385   if ((sock = prepare_connect_port(host, port)) > -1) {
386     printf("DCC chat established\n");
387     return select_loop(sock);
388   }
389   
390   return 1;
391 }
392
393 \f
394 int main (int argc, char **argv) {
395   char *host = "localhost";
396   char *action;
397   int c, status = 0;
398
399   progname = (char *)basename(argv[ 0 ]);
400
401         while (1)       {
402                 int this_option_optind = optind ? optind : 1;
403                 int option_index = 0;
404                 static struct option long_options[] =   {
405                         {"version", 0, 0, 'v'},
406                         {"help", 0, 0, 'h'},
407                         {0, 0, 0, 0}
408                 };
409                         
410                 c = getopt_long (argc, argv, "vh", long_options, &option_index);
411                 if (c == -1)
412                         break;
413                 
414                 switch (c) {
415                 case 'v':
416                         version();
417                         exit(0);
418                         break;
419                 case 'h':
420                         usage();
421                         exit(0);
422                         break;
423                 default:
424                         break;
425                 }
426         }
427
428   if (argc > 1) {
429     action = argv[ 1 ];
430   } else {
431     usage();
432     exit(1);
433   }
434
435   if (!strcmp(action, "resolve")) {
436     if (argc < 3) {
437       usage();
438       exit(1);
439     } else {
440       u_long i, addr;
441       for (i = 2; i < argc; i++) {
442         addr = primary_address_of(argv[i]);
443         if (addr != -1)
444           printf("%u\n", addr);
445         else
446           printf("0\n");
447       }
448       status = 0;
449     }
450   }
451
452   if (!strcmp(action, "send")) {
453     if (argc != 4) {
454       usage();
455       exit(1);
456     }
457     status = send_file (atoi(argv[ 2 ]), argv[ 3 ]);
458   } else if (!strcmp(action, "receive")) {
459     if (argc != 6) {
460       usage();
461       exit(1);
462     }
463     status = 
464       receive_file (extract_addr_of_string(argv[ 2 ]),
465                     atoi(argv[ 3 ]), atoi(argv[ 4 ]), argv[ 5 ]);
466   } else if (!strcmp(action, "chat")) {
467     if (argc > 3) {
468       if (!strcmp(argv[ 2 ], "listen")) {
469         if (argc != 4) {
470           usage();
471           exit(1);
472         }
473         status = chat_listen(atoi(argv[ 3 ]));
474       } else if (!strcmp(argv[ 2 ], "connect")) {
475         if (argc != 5) {
476           usage();
477           exit(1);
478         }
479         status = chat_connect(extract_addr_of_string(argv[ 3 ]), 
480                               atoi(argv[ 4 ]));
481       } else {
482         usage();
483         exit(1);
484       } 
485     }
486   } else {
487     usage();
488     exit(1);
489   }
490
491   return status;
492 }
493
494 /*
495  * Local variables:
496  *  compile-command: "gcc -DHAVE_STRTOUL -Wall -O6 -o dcc dcc.c"
497  *  c-indent-level: 2
498  *  c-basic-offset: 2
499  *  tab-width: 2
500  * End:
501  */