Rename ldcc -> rdcc, ltcp -> rtcp.
[elisp/riece.git] / dcc / tcp.c
1 /* tcp.c - TCP/IP stream emulation for GNU Emacs.
2  * Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
3  * Copyright (C) 1998-2003  Daiki Ueno
4  *
5  * This file is part of Riece.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  */
21 /* This program is based on `tcp' comming from old GNUS distribution
22    written by Masanobu Umeda <umerin@mse.kyutech.ac.jp>. */
23
24 #include <sys/types.h>
25 #include <sys/time.h>
26 #include <sys/socket.h>
27 #include <sys/file.h>
28 #include <sys/ioctl.h>
29 #include <sys/stat.h>
30 #include <sys/time.h>
31 #include <netdb.h>
32 #include <stdio.h>
33 #include <signal.h>
34 #include <fcntl.h>
35 #include <netinet/in.h>
36 #define _GNU_SOURCE
37 #include <getopt.h>
38
39 #ifdef HAVE_BASENAME
40 # ifdef HAVE_LIBGEN_H
41 #  include <libgen.h>
42 #  ifdef basename
43 #   undef basename
44 #  endif
45 # endif
46 # include <string.h>
47 #else
48 # define basename(path) (rindex((path), '/') + 1)
49 #endif
50
51 #ifndef NI_MAXHOST
52 # define NI_MAXHOST 1025
53 #endif
54
55 static char *progname;
56
57 void version () {
58   fprintf(stderr,
59           "%s (%s) %s\n"
60           "Copyright (C) 1998-2003 Daiki Ueno\n"
61           "This is free software; see the source for copying conditions.  There is NO\n"
62           "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", 
63           progname, PACKAGE, VERSION);
64 }
65
66 void usage () {
67   fprintf(stderr, "Usage: %s [options] host [service]\n", progname);
68 }
69
70 \f
71 main (argc, argv)
72   int argc;
73   char *argv[];
74 {
75   struct protoent *proto;
76   int family, socktype;
77   struct sockaddr *addr;
78   size_t addrlen;
79 #ifdef HAVE_GETADDRINFO
80   struct addrinfo *in, hints;
81 #else
82   struct hostent *host;
83   struct servent *serv;
84   struct sockaddr_in sin;
85 #endif
86   char *hostname = NULL, *service = "ircd";
87   int port;
88   fd_set *readfds, *writefds;
89   int server, emacsIn = fileno (stdin), emacsOut = fileno (stdout); 
90   char buffer[1024], *retry;
91   int nbuffer, wret, false = 0;
92   int c;
93   
94   progname = (char *) basename (argv[0]);
95
96   while (1)
97     {
98       int this_option_optind = optind ? optind : 1;
99       int option_index = 0;
100       static struct option long_options[] =
101         {
102           {"version", 0, 0, 'v'},
103           {"help", 0, 0, 'h'},
104           {0, 0, 0, 0}
105         };
106     
107       c = getopt_long (argc, argv, "vh", long_options, &option_index);
108       if (c == -1)
109         break;
110     
111       switch (c)
112         {
113         case 'v':
114           version ();
115           exit (0);
116           break;
117         case 'h':
118           usage ();
119           exit (0);
120           break;
121         default:
122           break;
123         }
124     }
125   
126   if (argc < 2)
127     {
128       usage();
129       exit (1);
130     }
131   if (argc >= 2)
132     hostname = argv[1];
133   if (argc >= 3)
134     service = argv[2];
135   
136   proto = getprotobyname ("tcp");
137   if (!proto)
138     {
139       perror ("getprotobyname");
140       exit (1);
141     }
142
143 #ifdef HAVE_GETADDRINFO
144   memset (&hints, 0, sizeof (hints));
145   hints.ai_family = AF_UNSPEC;
146   hints.ai_socktype = SOCK_STREAM;
147   hints.ai_protocol = proto->p_proto;
148   if (getaddrinfo (hostname, service, &hints, &in) < 0)
149     {
150       perror ("getaddrinfo");
151       exit (1);
152     }
153   family = in->ai_family;
154   socktype = in->ai_socktype;
155   addr = in->ai_addr;
156   addrlen = in->ai_addrlen;
157   freeaddrinfo (in);
158 #else
159   memset (&sin, 0, sizeof (sin));
160   host = gethostbyname (hostname);
161   if (!host)
162     return -1;
163   memcpy (&sin.sin_addr, host->h_addr, host->h_length);
164   serv = getservbyname (service, proto->p_name);
165   if (serv)
166     sin.sin_port = htons (serv->s_port);
167   else if (isdigit (service[0]))
168     sin.sin_port = htons (atoi (service));
169   family = sin.sin_family = AF_INET;
170   socktype = SOCK_STREAM;
171   addr = (struct sockaddr *)&sin;
172   addrlen = sizeof (sin);
173 #endif
174
175   server = socket (family, socktype, 0);
176   if (server == -1)
177     {
178       perror ("socket");
179       exit (1);
180     }
181
182   setsockopt (server, SOL_SOCKET, SO_REUSEADDR, 
183               (const char *) &false, sizeof (false));
184
185   if (connect (server, addr, addrlen) < 0)
186     {
187       perror ("connect");
188       close (server);
189       exit (1);
190     }
191
192 #ifdef O_NDELAY
193   fcntl (server, F_SETFL, O_NDELAY);
194 #endif /* O_NDELAY */
195
196   /* Connection established. */
197
198   readfds = (fd_set *) calloc(server + 1, sizeof (fd_mask));
199   writefds = (fd_set *) calloc(server + 1, sizeof (fd_mask));
200
201   while (1)
202     {
203       FD_SET (server, readfds);
204       FD_SET (emacsIn, readfds);
205       if (select (server+1, readfds, NULL, NULL, NULL) == -1)
206         {
207           perror ("select");
208           exit (1);
209         }
210       if (FD_ISSET (emacsIn, readfds))
211         {
212           /* From Emacs */
213           nbuffer = read (emacsIn, buffer, sizeof buffer -1);
214
215           if (nbuffer == 0)
216             goto finish;
217           for (retry = buffer; nbuffer > 0; nbuffer -= wret, retry += wret)
218             {
219               FD_SET (server, writefds);
220               if (select (server+1, NULL, writefds, NULL, NULL) == -1)
221                 {
222                   perror ("select");
223                   exit (1);
224                 }
225               wret = write (server, retry, nbuffer);
226               if (wret < 0) goto finish;
227             }
228         }
229       if (FD_ISSET (server, readfds))
230         {
231           /* From NNTP server */
232           nbuffer = read (server, buffer, sizeof buffer -1);
233           if (nbuffer == 0)
234             goto finish;
235           for (retry = buffer; nbuffer > 0; nbuffer -= wret, retry += wret)
236             {
237               FD_SET (emacsOut, writefds);
238               if (select (emacsOut+1, NULL, writefds, NULL, NULL) == -1)
239                 {
240                   perror ("select");
241                   exit (1);
242                 }
243               wret = write (emacsOut, retry, nbuffer);
244               if (wret < 0) goto finish;
245             }
246         }
247     }
248
249   /* End of communication. */
250  finish:
251   close (server);
252   close (emacsIn);
253   close (emacsOut);
254   exit (0);
255 }