1 /**************************************************************************************************
2 $Header: /pub/cvsroot/yencode/src/ypost/sock.c,v 1.2 2002/03/21 04:58:31 bboy Exp $
3
4 Copyright (C) 2002 Don Moore <bboy@bboy.net>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (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
22 #include "ypost.h"
23
24
25 /* Current socket operation being performed (for timeout handler). */
26 static char *current_operation;
27
28 /* Macro to start a timeout condition block. */
29 #define START_TIMEOUT(s) \
30 if (opt_timeout) { \
31 current_operation = s; \
32 signal(SIGALRM, sock_timeout_handler); \
33 alarm(opt_timeout); \
34 }
35
36 /* Macro to stop a timeout condition block. */
37 #define STOP_TIMEOUT \
38 if (opt_timeout) { \
39 signal(SIGALRM, SIG_DFL); \
40 alarm(0); \
41 }
42
43 /* Read buffers grow by this many bytes at a time. */
44 #define BUFFER_INCREMENT 128
45
46 /* Current FD if sock_setfd() is used. */
47 int _sock_current_fd = -1;
48
49 #define Y_SOCKDEBUG_SEND 0
50 #define Y_SOCKDEBUG_RECV 1
51
52
53 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
54 SOCKDEBUG
55 If debug is enabled, this will output the data sent or received, hiding wierd control chars
56 and converting CR and LF to '\r' and '\n'.
57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
58 static void
sockdebug(int direction,unsigned char * buf,size_t buflen)59 sockdebug(int direction, unsigned char *buf, size_t buflen)
60 {
61 register int ct;
62
63 if (!buf)
64 return;
65
66 if (direction == Y_SOCKDEBUG_RECV)
67 fputs("[DEBUG] IN: `", stderr);
68 else
69 fputs("[DEBUG] OUT: `", stderr);
70
71 for (ct = 0; ct < buflen; ct++)
72 {
73 if (buf[ct] == CR)
74 {
75 fputc('\\', stderr);
76 fputc('r', stderr);
77 }
78 else if (buf[ct] == LF)
79 {
80 fputc('\\', stderr);
81 fputc('n', stderr);
82 }
83 // else if (iscntrl(buf[ct]))
84 // fputc('?', stderr);
85 else
86 fputc(buf[ct], stderr);
87 }
88 fputc('\'', stderr);
89 fputc('\n', stderr);
90 fflush(stderr);
91 }
92 /*--- sockdebug() -------------------------------------------------------------------------------*/
93
94
95 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
96 SOCK_TIMEOUT_HANDLER
97 Called if socket operations time out.
98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
99 void
sock_timeout_handler(int signo)100 sock_timeout_handler(int signo)
101 {
102 Err("%s: %s\n", current_operation ? current_operation : _("socket operation"),
103 _("specified timeout exceeded"));
104 exit(EXIT_FAILURE);
105 }
106 /*--- sock_timeout_handler() --------------------------------------------------------------------*/
107
108
109 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
110 Given a host and port, which should be specified in the format "host:port", constructs and
111 returns a sockaddr_in record.
112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
113 struct sockaddr_in *
sock_getsockaddr(char * address)114 sock_getsockaddr(char *address)
115 {
116 static struct sockaddr_in sa; // Socket structureA
117 struct servent *ent; // Service entry
118 struct hostent *he; // Host entry
119 char *hostname, *portname; // Host and port part of `hostport'
120
121 memset(&sa, 0, sizeof(struct sockaddr_in)); // Reset socket structure
122 sa.sin_family = AF_INET;
123 portname = address;
124 hostname = strsep(&portname, ":");
125 if (!hostname)
126 Err("%s: %s", address, _("invalid server name"));
127
128 if (portname)
129 sa.sin_port = htons(atoi(portname));
130 else
131 {
132 START_TIMEOUT(_("service entry lookup"));
133 if (!(ent = getservbyname("nntp", "tcp")))
134 Err(_("unable to get default port for service `nntp/tcp'"));
135 STOP_TIMEOUT;
136 sa.sin_port = ent->s_port;
137 }
138
139 START_TIMEOUT(_("server name resolution"));
140 if (!(he = gethostbyname(hostname)))
141 Err("%s: %s", address, _("unable to resolve server address"));
142 STOP_TIMEOUT;
143
144 memcpy(&sa.sin_addr, he->h_addr_list[0], sizeof(struct in_addr));
145 return (&sa);
146 }
147 /*--- sock_getsockaddr() ------------------------------------------------------------------------*/
148
149
150 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
151 Opens a connection to the specified address. Returns the new file descriptor. Errors fatal.
152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
153 int
sock_open(struct sockaddr_in * sa)154 sock_open(struct sockaddr_in *sa)
155 {
156 int fd; // File descriptor of new socket
157 int rv; // Return value from connect
158
159 START_TIMEOUT(_("socket creation"));
160 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) // Create socket
161 ErrERR(_("error creating socket"));
162 STOP_TIMEOUT;
163
164 START_TIMEOUT(_("socket connection"));
165 while ((rv = connect(fd, (struct sockaddr *)sa, sizeof(struct sockaddr_in))) != 0)
166 {
167 if (errno == EAGAIN)
168 continue;
169 else
170 ErrERR(_("error connecting to server"));
171 }
172 STOP_TIMEOUT;
173 return (fd);
174 }
175 /*--- sock_open() -------------------------------------------------------------------------------*/
176
177
178 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
179 SOCK_READ_FD
180 Reads from the socket, obeying timeout. Returns number of bytes read.
181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
182 ssize_t
sock_read_fd(int fd,unsigned char * buf,size_t count)183 sock_read_fd(int fd, unsigned char *buf, size_t count)
184 {
185 int rv; // Return value from read()
186
187 memset(buf, 0, count); // XXX Is this necessary?
188
189 START_TIMEOUT(_("socket read"));
190 rv = read(fd, buf, count);
191 STOP_TIMEOUT;
192
193 if (rv < 0) // Will this fail falsely on EAGAIN?
194 ErrERR(_("error reading from socket"));
195
196 return (rv);
197 }
198 /*--- sock_read_fd() ----------------------------------------------------------------------------*/
199
200
201 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
202 SOCK_GETS_FD
203 Reads from the specified descriptor until '\n' is read. Returns a pointer to a dynamically
204 allocated buffer, which should be free()'d when no longer needed.
205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
206 unsigned char *
sock_gets_fd(int fd)207 sock_gets_fd(int fd)
208 {
209 int rv; // Return value from select
210 int offset = 0; // Number of characters read
211 int buflen = 0; // Current size of input buffer
212 unsigned char *buf; // Input buffer
213
214 // Start our input buffer off
215 buf = (unsigned char *)xmalloc(BUFFER_INCREMENT, 1);
216 memset(buf, 0, BUFFER_INCREMENT);
217 buflen = BUFFER_INCREMENT;
218
219 for (;;)
220 {
221 START_TIMEOUT(_("socket read"));
222 if (!(rv = sock_read_fd(fd, buf + offset, 1))) // Read one char at a time
223 Err(_("connection closed by remote"));
224 STOP_TIMEOUT;
225
226 if (buf[offset] == LF) // Are we done?
227 break;
228
229 // Grow the buffer if necessary
230 if (++offset == buflen - 1)
231 {
232 buf = xrealloc(buf, buflen + BUFFER_INCREMENT);
233 memset(buf + buflen, 0, BUFFER_INCREMENT);
234 buflen += BUFFER_INCREMENT;
235 }
236 };
237
238 if (opt_debug)
239 sockdebug(Y_SOCKDEBUG_RECV, buf, strlen(buf));
240
241 while ((buf[offset] == CR) || (buf[offset] == LF))
242 buf[offset--] = (unsigned char)'\0';
243
244 return (buf);
245 }
246 /*--- sock_gets_fd() ----------------------------------------------------------------------------*/
247
248
249 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
250 SOCK_WRITE_FD
251 Writes to the socket, obeying timeout.
252 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
253 void
sock_write_fd(int fd,const unsigned char * buf,int count)254 sock_write_fd(int fd, const unsigned char *buf, int count)
255 {
256 int offset = 0; // Number of bytes written
257 int rv; // Return value from write()
258
259 do
260 {
261 START_TIMEOUT(_("socket write"));
262 rv = write(fd, buf + offset, count - offset);
263 STOP_TIMEOUT;
264 if (rv == 0)
265 Err(_("connection closed by remote"));
266 if (rv < 0)
267 {
268 if (errno == EAGAIN)
269 continue;
270 Err(_("error writing to socket"));
271 }
272 if (opt_debug)
273 sockdebug(Y_SOCKDEBUG_SEND, (unsigned char *)(buf+offset), rv);
274 offset += rv;
275
276 } while (offset < count);
277 }
278 /*--- sock_write_fd() ---------------------------------------------------------------------------*/
279
280
281 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
282 SOCK_PUTS_FD
283 Writes a line to the descriptor. Appends CR/LF if necessary.
284 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
285 void
sock_puts_fd(int fd,const unsigned char * data)286 sock_puts_fd(int fd, const unsigned char *data)
287 {
288 int datalen = strlen(data);
289
290 if (!strstr(data, CRLF)) // Append CR/LF if necessary
291 {
292 unsigned char *buf;
293
294 buf = (unsigned char *)xmalloc((datalen + 3) * sizeof(unsigned char));
295 memcpy(buf, data, datalen);
296 buf[datalen] = CR;
297 buf[datalen+1] = LF;
298 buf[datalen+2] = (unsigned char)'\0';
299 sock_write_fd(fd, buf, datalen + 2);
300 free(buf);
301 }
302 else
303 sock_write_fd(fd, data, datalen);
304 }
305 /*--- sock_puts_fd() ----------------------------------------------------------------------------*/
306
307
308 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
309 SOCK_PRINTF
310 Only works if _sock_current_fd is set.
311 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
312 void
sock_printf(const char * fmt,...)313 sock_printf(const char *fmt, ...)
314 {
315 va_list ap;
316 size_t len;
317 unsigned char buf[BUFSIZ];
318 unsigned char *outbuf;
319 register unsigned char *i, *o;
320
321 if (_SOCK_CURRENT_FD == -1)
322 Err(_("unable to write; not connected to remote server"));
323
324 va_start(ap, fmt);
325 len = vsnprintf(buf, sizeof(buf)-4, fmt, ap);
326 va_end(ap);
327
328 /* Remove CR, convert LF to CRLF */
329 outbuf = (unsigned char *)xmalloc(((len * 2) + 1) * sizeof(unsigned char));
330 for (i = buf, o = outbuf; *i; i++)
331 {
332 if (*i == CR)
333 continue;
334 else if (*i == LF)
335 {
336 *(o++) = CR;
337 *(o++) = LF;
338 }
339 else
340 *(o++) = *i;
341 }
342 *o = '\0';
343 sock_puts_fd(_SOCK_CURRENT_FD, outbuf);
344 free(outbuf);
345 }
346 /*--- sock_printf() -----------------------------------------------------------------------------*/
347
348
349 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
350 SOCK_PUTS_METER_FD
351 Writes a line to the descriptor, displaying a meter. Appends CR/LF if necessary.
352 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
353 long
sock_puts_meter_fd(int fd,const unsigned char * buf,long current,long total,const char * desc)354 sock_puts_meter_fd(int fd, const unsigned char *buf, long current, long total, const char *desc)
355 {
356 int datalen = strlen(buf); // Length of data to write
357 unsigned char *data = NULL; // The data to send
358 register int offset = 0; // Number of bytes written
359 int rv; // Return value from write()
360
361 if (!strstr(buf, CRLF)) // Append CR/LF if necessary
362 {
363 data = (unsigned char *)xmalloc((datalen + 3) * sizeof(unsigned char));
364 memcpy(data, buf, datalen);
365 data[datalen] = CR;
366 data[datalen+1] = LF;
367 data[datalen+2] = (unsigned char)'\0';
368 datalen += 2;
369 }
370 else
371 data = (unsigned char *)xstrdup(buf);
372
373 /* Write the data */
374 do
375 {
376 START_TIMEOUT(_("socket write"));
377 rv = write(fd, data + offset, datalen - offset);
378 STOP_TIMEOUT;
379 if (rv == 0)
380 Err(_("connection closed by remote"));
381 if (rv < 0)
382 {
383 if (errno == EAGAIN)
384 continue;
385 Err(_("error writing to socket"));
386 }
387 if (opt_debug)
388 sockdebug(Y_SOCKDEBUG_SEND, (unsigned char *)(data+offset), rv);
389 offset += rv;
390 meter(current + offset, total, desc);
391 } while (offset < datalen);
392
393 free(data);
394 return (offset);
395 }
396 /*--- sock_puts_fd() ----------------------------------------------------------------------------*/
397
398
399 /* vi:set ts=3: */
400