isc_tcp.c 8.92 KB
Newer Older
Per Cederqvist's avatar
Per Cederqvist committed
1
2
3
/*
** isc_tcp.c                          Routines to handle TCP ISC sessions
**
Per Cederqvist's avatar
Per Cederqvist committed
4
** Copyright (C) 1992, 1998-1999, 2001 by Peter Eriksson and
Per Cederqvist's avatar
Per Cederqvist committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
** Per Cederqvist of the Lysator Academic Computer Association.
**
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Library General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** Library General Public License for more details.
**
** You should have received a copy of the GNU Library General Public
** License along with this library; if not, write to the Free
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Per Cederqvist's avatar
Per Cederqvist committed
21
22
23
**
** history:
** 920209 pen      code extracted from isc_session.c
Per Cederqvist's avatar
Per Cederqvist committed
24
** (See ChangeLog for recent history)
Per Cederqvist's avatar
Per Cederqvist committed
25
26
27
*/

#include <errno.h>
28
29
30
#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
#endif
Per Cederqvist's avatar
Per Cederqvist committed
31
32
33
#ifdef HAVE_STDDEF_H
#  include <stddef.h>
#endif
34
35
36
#ifdef HAVE_STDARG_H
#  include <stdarg.h>
#endif
Per Cederqvist's avatar
Per Cederqvist committed
37
38
39
40
41
42
#include <ctype.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/file.h>
#include <sys/ioctl.h>
43
#include <sys/socket.h>
Per Cederqvist's avatar
Per Cederqvist committed
44
#include <netdb.h>
45
46
47
#ifdef HAVE_STRING_H
#  include <string.h>
#endif
Per Cederqvist's avatar
Per Cederqvist committed
48
#include <fcntl.h>
49
50
51
#ifndef NULL
#  include <stdio.h>
#endif
Per Cederqvist's avatar
Per Cederqvist committed
52
53
54
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
Per Cederqvist's avatar
Per Cederqvist committed
55
#include <time.h>
56
57
58
#include <assert.h>

#include "oop.h"
59
60
#include "adns.h"
#include "oop-adns.h"
61
62

#include "s-string.h"
Per Cederqvist's avatar
Per Cederqvist committed
63
64
65

#include "isc.h"
#include "intern.h"
Per Cederqvist's avatar
Per Cederqvist committed
66
#include "isc_addr.h"
Per Cederqvist's avatar
Per Cederqvist committed
67
#include "unused.h"
Per Cederqvist's avatar
Per Cederqvist committed
68

69
static oop_call_fd isc_accept_cb;
Per Cederqvist's avatar
Per Cederqvist committed
70

71
72
static struct isc_scb_internal *
isc_tcp_accept(struct isc_scb_internal *scb)
Per Cederqvist's avatar
Per Cederqvist committed
73
{
74
  struct isc_scb_internal *new_scb;
Per Cederqvist's avatar
Per Cederqvist committed
75
  int fd;
76
77
  SOCKADDR_STORAGE addr;
  socklen_t len;
Per Cederqvist's avatar
Per Cederqvist committed
78
79
  

80
  len = sizeof(addr);
81
  if ((fd = accept(scb->pub.fd,
82
83
		   (struct sockaddr *)&addr,
		   &len)) < 0)
84
85
  {
    /* FIXME (bug 106): Log a message. */
Per Cederqvist's avatar
Per Cederqvist committed
86
    return NULL;
87
  }
Per Cederqvist's avatar
Per Cederqvist committed
88
  
89
  new_scb = isc_createtcp(scb->pub.master, scb->cfg, fd, ISC_STATE_RUNNING);
Per Cederqvist's avatar
Per Cederqvist committed
90
91
92
93
  if (!new_scb)
    return NULL;
  
  /* Fill in the session info structure */
94
95
  new_scb->pub.raddr = isc_mkipaddress(&addr);
  new_scb->pub.laddr = isc_copyaddress(scb->pub.laddr);
Per Cederqvist's avatar
Per Cederqvist committed
96
97
98
99
100
101
102
103
104
  
  return new_scb;
}



/*
** Create a TCP Session Address
*/
105
static union isc_address *
106
107
108
isc_mktcpaddress_internal(const char *address,
			  const char *service,
			  int address_family)
Per Cederqvist's avatar
Per Cederqvist committed
109
{
110
  union sockaddrs addr;
111
  struct hostent *hp = NULL;
Per Cederqvist's avatar
Per Cederqvist committed
112
  struct servent *sp;
113
114
115
#ifdef USE_GETIPNODEBYNAME
  int error_num;
#endif
Per Cederqvist's avatar
Per Cederqvist committed
116
117
118
119
120
121
122


  memset(&addr, 0, sizeof(addr));
  
  /* Any local address? */
  if (address == NULL)
  {
123
124
    addr.sa.sa_family = address_family;
    STORE_ADDR(addr, htonl(INADDR_ANY), in6addr_any);
Per Cederqvist's avatar
Per Cederqvist committed
125
  }
126
127
  else if (isdigit((int)(unsigned char)address[0])
	   || (address[0] == ':'))
Per Cederqvist's avatar
Per Cederqvist committed
128
  {
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#ifdef HAVE_INET_PTON
    if (FOR_EACH_AF(addr.sa.sa_family,
		    (address_family != addr.sa.sa_family)
		    || (inet_pton(address_family,
				  address,
				  CHOOSE_IP4OR6(addr,
						(void *) &addr.sa_in.sin_addr,
						(void *) &addr.sa_in6.sin6_addr)
				  ) <= 0)))
      return NULL;
#else
    /* No inet_pton; we have to do with inet_aton which
       only handles AF_INET addresses */
    if (address_family != AF_INET)
      return NULL;
    addr.sa.sa_family = AF_INET;
    if (!inet_aton(address, &addr.sa_in.sin_addr))
      return NULL;
#endif
Per Cederqvist's avatar
Per Cederqvist committed
148
  }
149
#ifdef USE_GETIPNODEBYNAME
150
151
  else if ((hp = getipnodebyname(address,
				 address_family,
152
153
154
155
				 /* Use AI_ADDRCONFIG and not
				    AI_DEFAULT, since this address is
				    only used for local addresses of
				    this host. */
156
157
158
159
				 AI_ADDRCONFIG,
				 &error_num)) == NULL)
    return NULL;
#elif defined(HAVE_GETHOSTBYNAME2)
160
  else if ((hp = gethostbyname2(address, address_family)) == NULL)
161
162
    return NULL;
#else
Per Cederqvist's avatar
Per Cederqvist committed
163
164
  else if ((hp = gethostbyname(address)) == NULL)
    return NULL;
165
166
#endif
  else 
Per Cederqvist's avatar
Per Cederqvist committed
167
  {
168
169
170
171
172
173
    addr.sa.sa_family = hp->h_addrtype;
    memcpy(CHOOSE_IP4OR6(addr,
			 (void *) &addr.sa_in.sin_addr,
			 (void *) &addr.sa_in6.sin6_addr),
	   hp->h_addr,
	   hp->h_length);
Per Cederqvist's avatar
Per Cederqvist committed
174
175
  }
  
176
177
178
#ifdef USE_GETIPNODEBYNAME
  if (hp != NULL)
    freehostent(hp);
179
180
181
182
183
#endif

  if (addr.sa.sa_family != address_family)
    return NULL;
    
Per Cederqvist's avatar
Per Cederqvist committed
184
  if (isdigit((int)(unsigned char)service[0]))
185
186
187
    CHOOSE_IP4OR6(addr,
		  addr.sa_in.sin_port = htons(atoi(service)),
		  addr.sa_in6.sin6_port = htons(atoi(service)));
Per Cederqvist's avatar
Per Cederqvist committed
188
189
190
  else if ((sp = getservbyname(service, "tcp")) == NULL)
    return NULL;
  else
191
192
193
194
195
    CHOOSE_IP4OR6(addr,
		  addr.sa_in.sin_port = sp->s_port,
		  addr.sa_in6.sin6_port = sp->s_port);
  
  return isc_mkipaddress(&addr.ssa);
Per Cederqvist's avatar
Per Cederqvist committed
196
197
}

198
199
200
201
202
203
204
union isc_address *
isc_mktcpaddress(const char *address,
		 const char *service)
{
  union isc_address *ia;
  int af;

205
206
207
208
  if (FOR_EACH_AF(
	  af,
	  (ia = isc_mktcpaddress_internal(address, service, af)) == NULL))
      return NULL;
209
210
211
212
213

  return ia;
}


214
int
215
216
217
218
219
220
221
isc_addresssize(union isc_address *UNUSED_UNLESS_INET6(ia))
{
    return CHOOSE_IP4OR6(ia->saddr,
			 sizeof(struct sockaddr_in),
			 sizeof(struct sockaddr_in6));
}

222
int
223
224
225
226
227
228
229
230
231
232
233
isc_addressfamily(union isc_address *ia)
{
    return ia->saddr.sa.sa_family;
}


struct sockaddr *
isc_addresspointer(union isc_address *ia)
{
    return (struct sockaddr *) &ia->saddr;
}
Per Cederqvist's avatar
Per Cederqvist committed
234
235
236

		  
/*
237
238
** Create a TCP session.
** Will close fd and return NULL if an error occurs.
Per Cederqvist's avatar
Per Cederqvist committed
239
*/
240
struct isc_scb_internal *
241
242
243
244
isc_createtcp(struct isc_mcb *mcb,
	      struct isc_session_cfg *cfg,
	      int fd,
	      enum isc_session_state initial_state)
Per Cederqvist's avatar
Per Cederqvist committed
245
{
246
  struct isc_scb_internal *scb;
Per Cederqvist's avatar
Per Cederqvist committed
247
  int res;
248
249
  int flag;
  struct linger ling;
Per Cederqvist's avatar
Per Cederqvist committed
250
251
252
  

  if (fd == -1)
253
254
255
256
  {
    int protocol_family;
    if (FOR_EACH_PF(protocol_family,
		    (fd = socket(protocol_family, SOCK_STREAM, 0)) < 0))
257
258
    {
      /* FIXME (bug 106): Log a message. */
Per Cederqvist's avatar
Per Cederqvist committed
259
      return NULL;
260
    }
261
  }
262
263

  fd = isc_relocate_fd(fd, cfg->fd_relocate);
Per Cederqvist's avatar
Per Cederqvist committed
264
265
266
267
  
  /* Set non blocking write mode */
  if ((res = fcntl(fd, F_GETFL, 0)) == -1)
  {
268
    /* FIXME (bug 106): Log a message. */
Per Cederqvist's avatar
Per Cederqvist committed
269
270
271
272
    close(fd);
    return NULL;
  }
    
Per Cederqvist's avatar
Per Cederqvist committed
273
274
275
276
  /* If compilation fails on the next line, please report it as a bug
     to ceder@lysator.liu.se.  I'd like to talk to you so that you can
     test an autoconf solution to this problem.  As a workaround, you
     can change "O_NONBLOCK" to "FNDELAY". */
277
  if (fcntl(fd, F_SETFL, res | O_NONBLOCK) == -1)
Per Cederqvist's avatar
Per Cederqvist committed
278
  {
279
    /* FIXME (bug 106): Log a message. */
Per Cederqvist's avatar
Per Cederqvist committed
280
281
282
    close(fd);
    return NULL;
  }
283

Per Cederqvist's avatar
Per Cederqvist committed
284
  /* This is the modern way to turn off linger and turn on reuseaddr. */
285
286
287
288
289
  ling.l_onoff = 0;
  ling.l_linger = 0;
  setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); 
  flag = 1;
  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
Per Cederqvist's avatar
Per Cederqvist committed
290
  
291
  scb = isc_create(mcb, cfg, initial_state);
Per Cederqvist's avatar
Per Cederqvist committed
292
  if (!scb)
293
  {
294
    /* FIXME (bug 106): Log a message. */
295
    close(fd);
Per Cederqvist's avatar
Per Cederqvist committed
296
    return NULL;
297
  }
Per Cederqvist's avatar
Per Cederqvist committed
298

299
  scb->pub.fd = fd;
Per Cederqvist's avatar
Per Cederqvist committed
300
  
301
302
  scb->pub.raddr = NULL;
  scb->pub.laddr = NULL;
Per Cederqvist's avatar
Per Cederqvist committed
303
304
305
306
307
308
    
  return scb;
}



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
static int
mkaddr_and_bind(const char *address,
		const char *service,
		int fd,
		int af,
		union isc_address **ia_out)
{
  union isc_address *ia = isc_mktcpaddress_internal(address, service, af);
  if (ia == NULL)
    return 1;

  if (bind(fd, isc_addresspointer(ia), isc_addresssize(ia)) < 0)
    {
      isc_freeaddress(ia);
      return 1;
    }

  *ia_out = ia;
  return 0;
}

Per Cederqvist's avatar
Per Cederqvist committed
330
331
332
333
/*
** Bind a TCP session to a local port and address
*/
int
334
isc_bindtcp(struct isc_scb_internal *scb,
Per Cederqvist's avatar
Per Cederqvist committed
335
336
337
	    const char *address,
	    const char *service)
{
338
  union isc_address *ia;
339
  int af;
Per Cederqvist's avatar
Per Cederqvist committed
340
341


342
  if (FOR_EACH_AF(af, mkaddr_and_bind(address, service, scb->pub.fd, af, &ia)))
Per Cederqvist's avatar
Per Cederqvist committed
343
344
    return -1;

345
  scb->pub.laddr = ia;
346
  assert(scb->pub.raddr == NULL);
Per Cederqvist's avatar
Per Cederqvist committed
347
348
349
350
351
    
  return 0;
}


352
353
354
355
356
static void *
isc_accept_cb(oop_source *UNUSED(source),
	      int fd,
	      oop_event event,
	      void *user)
Per Cederqvist's avatar
Per Cederqvist committed
357
{
358
359
  struct isc_scb_internal *scb = (struct isc_scb_internal*)user;
  struct isc_scb_internal *new_scb;
Per Cederqvist's avatar
Per Cederqvist committed
360

361
362
  assert(event == OOP_READ);
  assert(scb->state == ISC_STATE_LISTENING);
363
  assert(scb->pub.fd == fd);
Per Cederqvist's avatar
Per Cederqvist committed
364

365
366
367
  new_scb = isc_tcp_accept(scb);
  if (new_scb == NULL)
    return OOP_CONTINUE;
Per Cederqvist's avatar
Per Cederqvist committed
368

369
  isc_insert(scb->pub.master, new_scb);
370
  return scb->accept_cb(&scb->pub, &new_scb->pub);
Per Cederqvist's avatar
Per Cederqvist committed
371
372
373
374
375
376
}


/*
** Establish a port to listen at for new TCP connections
*/
377
378
struct isc_scb *
isc_listentcp(struct isc_mcb *mcb,
Per Cederqvist's avatar
Per Cederqvist committed
379
	      const char *address,
380
381
	      const char *service,
	      isc_accept_callback *cb)
Per Cederqvist's avatar
Per Cederqvist committed
382
{
383
  struct isc_scb_internal *scb;
Per Cederqvist's avatar
Per Cederqvist committed
384
385
386
  int errcode;
  

387
  scb = isc_createtcp(mcb, mcb->scfg, -1, ISC_STATE_LISTENING);
Per Cederqvist's avatar
Per Cederqvist committed
388
389
390
  if (!scb)
    return NULL;

391
392
  errno = 0;
  if (isc_bindtcp(scb, address, service) < 0)
Per Cederqvist's avatar
Per Cederqvist committed
393
394
  {
    errcode = errno;
395
    isc_destroy(NULL, &scb->pub);
Per Cederqvist's avatar
Per Cederqvist committed
396
397
398
399
    errno = errcode;
    return NULL;
  }

400
  if (listen(scb->pub.fd, scb->cfg->max.backlog) < 0)
Per Cederqvist's avatar
Per Cederqvist committed
401
402
  {
    errcode = errno;
403
    isc_destroy(NULL, &scb->pub);
Per Cederqvist's avatar
Per Cederqvist committed
404
405
406
407
408
    errno = errcode;
    return NULL;
  }

  (void) isc_insert(mcb, scb);
409
  scb->accept_cb = cb;
410
  mcb->event_source->on_fd(mcb->event_source, scb->pub.fd, OOP_READ,
411
			   isc_accept_cb, scb);
Per Cederqvist's avatar
Per Cederqvist committed
412
  
413
  return &scb->pub;
Per Cederqvist's avatar
Per Cederqvist committed
414
}