Initial revision
[riece] / 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-2002  Daiki Ueno
4  *
5  * This file is part of Liece.
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         printf("%s (Liece) 1.4.0\n"
59                "Copyright (C) 1998, 1999 Daiki Ueno\n"
60                "This is free software; see the source for copying conditions.  There is NO\n"
61                "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", 
62                progname);
63 }
64
65 void usage() {
66   printf("Usage: %s [options] host [service]\n", progname);
67 }
68
69 \f
70 main (argc, argv)
71   int argc;
72   char *argv[];
73 {
74   struct protoent *proto;
75   int family, socktype;
76   struct sockaddr *addr;
77   size_t addrlen;
78 #ifdef HAVE_GETADDRINFO
79   struct addrinfo *in, hints;
80 #else
81   struct hostent *host;
82   struct servent *serv;
83   struct sockaddr_in sin;
84 #endif
85   char *hostname = NULL, *service = "ircd";
86   int port;
87   fd_set *readfds, *writefds;
88   int server, emacsIn = fileno (stdin), emacsOut = fileno (stdout); 
89   char buffer[1024], *retry;
90   int nbuffer, wret, false = 0;
91   int c;
92   
93   progname = (char *) basename (argv[0]);
94
95   while (1)
96     {
97       int this_option_optind = optind ? optind : 1;
98       int option_index = 0;
99       static struct option long_options[] =
100         {
101           {"version", 0, 0, 'v'},
102           {"help", 0, 0, 'h'},
103           {0, 0, 0, 0}
104         };
105     
106       c = getopt_long (argc, argv, "vh", long_options, &option_index);
107       if (c == -1)
108         break;
109     
110       switch (c)
111         {
112         case 'v':
113           version ();
114           exit (1);
115           break;
116         case 'h':
117           usage ();
118           exit (1);
119           break;
120         default:
121           break;
122         }
123     }
124   
125   if (argc < 2)
126     {
127       usage();
128       exit (1);
129     }
130   if (argc >= 2)
131     hostname = argv[1];
132   if (argc >= 3)
133     service = argv[2];
134   
135   proto = getprotobyname ("tcp");
136   if (!proto)
137     {
138       perror ("getprotobyname");
139       exit (1);
140     }
141
142 #ifdef HAVE_GETADDRINFO
143   memset (&hints, 0, sizeof (hints));
144   hints.ai_family = AF_UNSPEC;
145   hints.ai_socktype = SOCK_STREAM;
146   hints.ai_protocol = proto->p_proto;
147   if (getaddrinfo (hostname, service, &hints, &in) < 0)
148     {
149       perror ("getaddrinfo");
150       exit (1);
151     }
152   family = in->ai_family;
153   socktype = in->ai_socktype;
154   addr = in->ai_addr;
155   addrlen = in->ai_addrlen;
156   freeaddrinfo (in);
157 #else
158   memset (&sin, 0, sizeof (sin));
159   host = gethostbyname (hostname);
160   if (!host)
161     return -1;
162   memcpy (&sin.sin_addr, host->h_addr, host->h_length);
163   serv = getservbyname (service, proto->p_name);
164   if (serv)
165     sin.sin_port = htons (serv->s_port);
166   else if (isdigit (service[0]))
167     sin.sin_port = htons (atoi (service));
168   family = sin.sin_family = AF_INET;
169   socktype = SOCK_STREAM;
170   addr = (struct sockaddr *)&sin;
171   addrlen = sizeof (sin);
172 #endif
173
174   server = socket (family, socktype, 0);
175   if (server == -1)
176     {
177       perror ("socket");
178       exit (1);
179     }
180
181   setsockopt (server, SOL_SOCKET, SO_REUSEADDR, 
182               (const char *) &false, sizeof (false));
183
184   if (connect (server, addr, addrlen) < 0)
185     {
186       perror ("connect");
187       close (server);
188       exit (1);
189     }
190
191 #ifdef O_NDELAY
192   fcntl (server, F_SETFL, O_NDELAY);
193 #endif /* O_NDELAY */
194
195   /* Connection established. */
196
197   readfds = (fd_set *) calloc(server + 1, sizeof (fd_mask));
198   writefds = (fd_set *) calloc(server + 1, sizeof (fd_mask));
199
200   while (1)
201     {
202       FD_SET (server, readfds);
203       FD_SET (emacsIn, readfds);
204       if (select (server+1, readfds, NULL, NULL, NULL) == -1)
205         {
206           perror ("select");
207           exit (1);
208         }
209       if (FD_ISSET (emacsIn, readfds))
210         {
211           /* From Emacs */
212           nbuffer = read (emacsIn, buffer, sizeof buffer -1);
213
214           if (nbuffer == 0)
215             goto finish;
216           for (retry = buffer; nbuffer > 0; nbuffer -= wret, retry += wret)
217             {
218               FD_SET (server, writefds);
219               if (select (server+1, NULL, writefds, NULL, NULL) == -1)
220                 {
221                   perror ("select");
222                   exit (1);
223                 }
224               wret = write (server, retry, nbuffer);
225               if (wret < 0) goto finish;
226             }
227         }
228       if (FD_ISSET (server, readfds))
229         {
230           /* From NNTP server */
231           nbuffer = read (server, buffer, sizeof buffer -1);
232           if (nbuffer == 0)
233             goto finish;
234           for (retry = buffer; nbuffer > 0; nbuffer -= wret, retry += wret)
235             {
236               FD_SET (emacsOut, writefds);
237               if (select (emacsOut+1, NULL, writefds, NULL, NULL) == -1)
238                 {
239                   perror ("select");
240                   exit (1);
241                 }
242               wret = write (emacsOut, retry, nbuffer);
243               if (wret < 0) goto finish;
244             }
245         }
246     }
247
248   /* End of communication. */
249  finish:
250   close (server);
251   close (emacsIn);
252   close (emacsOut);
253   exit (0);
254 }