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