forked from mbert/elvis
/
osnet.c
368 lines (320 loc) · 9.33 KB
/
osnet.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/* osnet.c */
/* This function contains functions for accessing the internet. These are
* all protocol-independent, but (to some degree) OS-dependent. Specifically,
* the following functions are defined:
*
* sockbuf_t *netconnect(char *site_port, unsigned int defport);
* Open a socket connection to a specific port on a specific site.
* site_port is a host name or numbers-and-dots address, optionally
* followed by a port number. defport is the port number to use if
* site_port contains no port number.
*
* void netdisconnect(sockbuf_t *sb);
* Close a socket connection which was created via netconnect()
*
* ELVBOOL netread(sockbuf_t *sb);
* Read bytes from sb's socket. Returns ElvTrue if successful (even if
* no data has been read, at the end of input), or ElvFalse if error or
* user-abort (after giving an error message).
*
* char *netgetline(sockbuf_t *sb);
* Fetch the next line of text from a socket, and return it.
* Returns NULL if error or user-abort.
*
* char *netbuffer(sockbuf_t *sb);
* Return a pointer to the next byte in sb's read-buffer.
* (This is actually a macro, defined in osnet.h.)
*
* int netbytes(sockbuf_t *sb);
* Return the number of bytes in sb's read-buffer.
* (This is actually a macro, defined in osnet.h.)
*
* void netconsume(sockbuf_t *sb, int len);
* Consume len bytes from sb's read-buffer.
* (This is actually a macro, defined in osnet.h)
*
* ELVBOOL netwrite(sockbuf_t *sb, char *text, int bytes);
* Write data to sb's socket. Returns ElvTrue iff successful.
*
* ELVBOOL netputline(sockbuf_t *sb, char *text);
* Write a line to sb's socket. text is a NUL-terminated line;
* netlinewrite() adds a CR-LF pair itself.
*
* char *netself(void)
* Return the name of the local system.
*/
#include "elvis.h"
#ifdef FEATURE_RCSID
char id_osnet[] = "$Id: osnet.c,v 2.12 2003/10/17 17:41:23 steve Exp $";
#endif
#if defined(PROTOCOL_HTTP) || defined(PROTOCOL_FTP)
# define CHAR WinCHAR
# include <winsock.h>
# undef CHAR
# define close(s) closesocket(s)
# define read(s,p,n) recv(s, p, n, 0)
# define write(s,p,n) send(s, p, n, 0)
# if USE_PROTOTYPES
static ELVBOOL site2addr(char *site, struct in_addr *address);
# endif
static ELVBOOL initialized;
/* Find the address of a site. The site can be given as either a domain name,
* or a "numbers and dots" name. If successful, the address is stuffed into
* address struct, and ElvTrue is returned; otherwise ElvFalse is returned.
*/
static ELVBOOL site2addr(site, address)
char *site;
struct in_addr *address;
{
struct hostent *siteinfo;
static char prevsite[100];
static struct in_addr prevaddr;
/* if the site name starts with a digit, then assume it is in the
* "numbers and dots" format. Else use the name server.
*/
if (elvdigit(site[0]))
{
/* convert to binary address */
address->S_un.S_addr = inet_addr(site);
if (address->S_un.S_addr == INADDR_NONE)
goto Error;
}
else
{
/* if same name as last time, then just use the same address */
if (strlen(site) < QTY(prevsite) && !strcmp(prevsite, site))
{
*address = prevaddr;
return ElvTrue;
}
/* look up the name */
msg(MSG_STATUS, "[s]looking up site $1", site);
siteinfo = gethostbyname(site);
if (!siteinfo)
{
goto Error;
}
/* use the primary address */
memcpy(address, siteinfo->h_addr_list[0], siteinfo->h_length);
/* save the info about this site */
strncpy(prevsite, site, sizeof prevsite);
prevaddr = *address;
}
return ElvTrue;
Error:
msg(MSG_ERROR, "[s]unknown site $1", site);
return ElvFalse;
}
/* Open a connection to a given site and port.
* Returns a sockbuf_t pointer if successful, or NULL for errors (in which
* case it also gives an error message).
*/
sockbuf_t *netconnect(site_port, defport)
char *site_port; /* name of remote system, with optional port number */
unsigned int defport;
{
struct in_addr serverAddress;
struct sockaddr_in destPort;
char *tmp;
char buf[150];
sockbuf_t *sb;
/* If first time, then initialize WinSockets */
if (!initialized)
{
WORD wVersionRequested = MAKEWORD(1, 1);
WSADATA wsaData;
/* Try to start WinSock version 1.1 */
switch (WSAStartup(wVersionRequested, &wsaData))
{
case WSASYSNOTREADY:
msg(MSG_ERROR, "network not ready");
WSACleanup();
return NULL;
case WSAEINVAL:
msg(MSG_ERROR, "[dddd]your winsock supports $1.$2 - $3.$4",
(long)LOBYTE(wsaData.wVersion), (long)HIBYTE(wsaData.wVersion),
(long)LOBYTE(wsaData.wHighVersion), (long)HIBYTE(wsaData.wHighVersion));
/* fall through... */
case WSAVERNOTSUPPORTED:
msg(MSG_ERROR, "elvis requires winsock 1.1");
WSACleanup();
return NULL;
}
initialized = ElvTrue;
}
/* if site_port contains a port number, then separate it from the site
* name, and use the given port instead of the default port.
*/
strcpy(buf, site_port);
tmp = strchr(buf, ':');
if (tmp)
{
*tmp++ = '\0';
defport = atoi(tmp);
}
/* look up the address of the server */
if (!site2addr(buf, &serverAddress))
return NULL;
/* The remainder of this function connects to the server */
msg(MSG_STATUS, "[s]connecting to $1", buf);
/* create a socket, and a sockbuf_t to buffer it */
sb = safealloc(1, sizeof(sockbuf_t));
sb->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sb->fd < 0)
{
safefree(sb);
msg(MSG_ERROR, "could not create socket");
return NULL;
}
/* connect the socket to the remote machine */
destPort.sin_family = AF_INET;
destPort.sin_port = htons((unsigned short)defport);
destPort.sin_addr = serverAddress;
if (connect(sb->fd, (struct sockaddr *)&destPort, sizeof(destPort)))
{
netdisconnect(sb);
msg(MSG_ERROR, "could not connect");
return NULL;
}
return sb;
}
void netdisconnect(sb)
sockbuf_t *sb; /* buffered socket to be freed */
{
close(sb->fd);
safefree(sb);
}
/* Read as much data as possible from a socket, with a timeout. Returns True
* if successful, or ElvFalse if there was an error or the user aborted; in the
* latter cases, an error message is given.
*/
ELVBOOL netread(sockbuf_t *sb)
{
int i;
int gotfds;
fd_set rfds;
struct timeval timeout;
/* Wait for data, checking periodically for a user abort. */
do
{
/* try to read for up to 2 seconds */
FD_ZERO(&rfds);
FD_SET(sb->fd, &rfds);
timeout.tv_sec = 2;
timeout.tv_usec = 0;
gotfds = select(sb->fd + 1, &rfds, NULL, NULL, &timeout);
if (gotfds < 0 || guipoll(ElvFalse))
{
return ElvFalse;
}
} while (gotfds == 0);
/* remove any consumed text from the buffer */
if (sb->left > 0 && sb->right > sb->left)
/* regions may overlap -- it is guaranteed to work */
memcpy(sb->buf, &sb->buf[sb->left], sb->right - sb->left);
sb->right -= sb->left;
sb->left = 0;
/* Read as much data as is available */
i = read(sb->fd, &sb->buf[sb->right], sizeof sb->buf - sb->right);
if (i < 0)
i = read(sb->fd, &sb->buf[sb->right], sizeof sb->buf - sb->right);
if (i < 0)
{
msg(MSG_ERROR, "error reading from socket");
return ElvFalse;
}
sb->right += i;
/* return the number of bytes available */
return ElvTrue;
}
/* Read a line from a socket. Return the line if successful, NULL if error. */
char *netgetline(sockbuf_t *sb)
{
int i;
char *ret;
/* loop until something interesting happens */
for (;;)
{
/* see if we have a whole line now */
for (ret = netbuffer(sb), i = sb->left; i < sb->right; i++)
{
if (sb->buf[i] == '\n')
{
/* found end-of-line! Mark it & return. */
sb->buf[i] = '\0';
if (i >= 1 && sb->buf[i - 1] == '\r')
sb->buf[i - 1] = '\0';
sb->left = i + 1;
return ret;
}
}
/* read some more data */
i = netbytes(sb);
if (!netread(sb) || i == netbytes(sb))
return NULL;
}
/*NOTREACHED*/
}
/* Send data bytes through a socket. Returns ElvTrue if successful, or ElvFalse if
* error (after giving an error message).
*/
ELVBOOL netwrite(sb, data, len)
sockbuf_t *sb;
char *data;
int len;
{
if (write(sb->fd, data, len) == len)
return ElvTrue;
msg(MSG_ERROR, "transmission failed");
return ElvFalse;
}
/* Send a line through a socket. This intended to be used for sending commands
* to an FTP or HTTP server. The line should be a normal NUL-terminated string
* with no newline; this function appends a CRLF. Returns ElvTrue if successful,
* or ElvFalse if error (after giving an error message).
*/
ELVBOOL netputline(sb, command, arg1, arg2)
sockbuf_t *sb; /* stream to write to */
char *command; /* command name */
char *arg1, *arg2; /* arguments, may be NULL if not used */
{
int len;
char *buf;
/* combine the command and arg into one string */
if (arg2)
{
len = strlen(command) + strlen(arg1) + strlen(arg2) + 5;
buf = safealloc(len, sizeof(char));
sprintf(buf, "%s %s %s\r\n", command, arg1, arg2);
}
else if (arg1)
{
len = strlen(command) + strlen(arg1) + 4;
buf = safealloc(len, sizeof(char));
sprintf(buf, "%s %s\r\n", command, arg1);
}
else
{
len = strlen(command) + 3;
buf = safealloc(len, sizeof(char));
sprintf(buf, "%s\r\n", command);
}
len--; /* <-- so the NUL terminator isn't sent */
/* send the command to the server */
if (write(sb->fd, buf, len) != len && *command)
{
msg(MSG_ERROR, "could not send request to server");
safefree(buf);
return ElvFalse;
}
safefree(buf);
return ElvTrue;
}
char *netself P_((void))
{
static char name[50];
if (gethostname(name, sizeof(name)))
strcpy(name, "graceland.net");
return name;
}
#endif /* PROTOCOL_HTTP || PROTOCOL_FTP */