summaryrefslogtreecommitdiffstats
path: root/nsock/src/nsock_internal.h
blob: dc47c3f049409833ada62113fb53d8df914b3751 (plain)
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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
/***************************************************************************
 * nsock_internal.h -- PRIVATE interface definitions for the guts of the   *
 * nsock parallel socket event library. Applications calling this library  *
 * should NOT include this. even LOOK at these :).                         *
 *                                                                         *
 ***********************IMPORTANT NSOCK LICENSE TERMS***********************
 *
 * The nsock parallel socket event library is (C) 1999-2023 Nmap Software LLC
 * This library is free software; you may redistribute and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; Version 2. This guarantees your right to use, modify, and
 * redistribute this software under certain conditions. If this license is
 * unacceptable to you, Nmap Software LLC may be willing to sell alternative
 * licenses (contact sales@nmap.com ).
 *
 * As a special exception to the GPL terms, Nmap Software LLC grants permission
 * to link the code of this program with any version of the OpenSSL library
 * which is distributed under a license identical to that listed in the included
 * docs/licenses/OpenSSL.txt file, and distribute linked combinations including
 * the two. You must obey the GNU GPL in all respects for all of the code used
 * other than OpenSSL. If you modify this file, you may extend this exception to
 * your version of the file, but you are not obligated to do so.
 *
 * If you received these files with a written license agreement stating terms
 * other than the (GPL) terms above, then that alternative license agreement
 * takes precedence over this comment.
 *
 * Source is provided to this software because we believe users have a right to
 * know exactly what a program is going to do before they run it. This also
 * allows you to audit the software for security holes.
 *
 * Source code also allows you to port Nmap to new platforms, fix bugs, and add
 * new features. You are highly encouraged to send your changes to the
 * dev@nmap.org mailing list for possible incorporation into the main
 * distribution. By sending these changes to Fyodor or one of the Insecure.Org
 * development mailing lists, or checking them into the Nmap source code
 * repository, it is understood (unless you specify otherwise) that you are
 * offering the Nmap Project (Nmap Software LLC) the unlimited, non-exclusive
 * right to reuse, modify, and relicense the code. Nmap will always be available
 * Open Source, but this is important because the inability to relicense code
 * has caused devastating problems for other Free Software projects (such as KDE
 * and NASM). We also occasionally relicense the code to third parties as
 * discussed above. If you wish to specify special license conditions of your
 * contributions, just say so when you send them.
 *
 * This program 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 General Public License v2.0 for more
 * details (http://www.gnu.org/licenses/gpl-2.0.html).
 *
 ***************************************************************************/

/* $Id$ */

#ifndef NSOCK_INTERNAL_H
#define NSOCK_INTERNAL_H

#include <nbase.h>

#ifdef HAVE_CONFIG_H
#include "nsock_config.h"
#include "nbase_config.h"
#endif

#ifdef WIN32
#include "nbase_winconfig.h"
#include <Winsock2.h>
#endif

#include "gh_list.h"
#include "gh_heap.h"
#include "filespace.h"
#include "nsock.h" /* The public interface -- I need it for some enum defs */
#include "nsock_ssl.h"
#include "nsock_proxy.h"

#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#endif
#if HAVE_STRINGS_H
#include <strings.h>
#endif
#if HAVE_SYS_UN_H
#include <sys/un.h>
#endif

#ifndef IPPROTO_SCTP
#define IPPROTO_SCTP 132
#endif


/* ------------------- CONSTANTS ------------------- */
#define READ_BUFFER_SZ 8192

enum nsock_read_types {
  NSOCK_READLINES,
  NSOCK_READBYTES,
  NSOCK_READ
};

enum iod_state {
  NSIOD_STATE_DELETED,
  NSIOD_STATE_INITIAL,

  /* sd was provided to us in nsock_iod_new2 (see nsock_iod.c) */
  NSIOD_STATE_UNKNOWN,

  NSIOD_STATE_CONNECTED_TCP,
  NSIOD_STATE_CONNECTED_UDP
};

/* XXX: ensure that these values can be OR'ed when adding new ones */
#define EV_NONE   0x00
#define EV_READ   0x01
#define EV_WRITE  0x02
#define EV_EXCEPT 0x04


/* ------------------- STRUCTURES ------------------- */

struct readinfo {
  enum nsock_read_types read_type;
  /* num lines; num bytes; whatever (depends on read_type) */
  int num;
};

struct writeinfo {
  struct sockaddr_storage dest;
  size_t destlen;
  /* Number of bytes successfully written */
  int written_so_far;
};

/* Remember that callers of this library should NOT be accessing these
 * fields directly */
struct npool {
  /* User data, NULL if unset */
  void *userdata;

  /* IO Engine vtable */
  struct io_engine *engine;
  /* IO Engine internal data */
  void *engine_data;

  /* Active network events */
  gh_list_t connect_events;
  gh_list_t read_events;
  gh_list_t write_events;
#if HAVE_PCAP
  gh_list_t pcap_read_events;
#endif
  gh_heap_t expirables;

  /* Active iods and related lists of events */
  gh_list_t active_iods;

  /* struct niod structures that have been freed for reuse */
  gh_list_t free_iods;
  /* When an event is deleted, we stick it here for later reuse */
  gh_list_t free_events;

  /* Number of events pending (total) on all lists */
  int events_pending;

  /* Serial # of next event (used to create next nsock_event_id */
  unsigned long next_event_serial;
  /* Serial # of next iod to be created */
  unsigned long next_iod_serial;

  /* If nsock_loop() returns NSOCK_LOOP_ERROR, this is where we describe the
   * error (errnum fashion) */
  int errnum;

  /* If true, new sockets will have SO_BROADCAST set */
  int broadcast;

  /* Interface to bind to; only supported on Linux with SO_BINDTODEVICE sockopt. */
  const char *device;

  /* If true, exit the next iteration of nsock_loop with a status of
   * NSOCK_LOOP_QUIT. */
  int quit;

#if HAVE_OPENSSL
  /* The SSL Context (options and such) */
  SSL_CTX *sslctx;
#ifdef HAVE_DTLS_CLIENT_METHOD
  SSL_CTX *dtlsctx;
#endif
#endif

  /* Optional proxy chain (NULL is not set). Can only be set once per NSP (using
   * nsock_proxychain_new() or nsock_pool_set_proxychain(). */
  struct proxy_chain *px_chain;

};


/* nsock_iod is like a "file descriptor" for the nsock library.  You use it to
 * request events. */
struct niod {
  /* The socket descriptor related to the event */
  int sd;

  /* Number of pending events on this iod */
  int events_pending;

  /* Pending events */
  gh_lnode_t *first_connect;
  gh_lnode_t *first_read;
  gh_lnode_t *first_write;
#if HAVE_PCAP
  gh_lnode_t *first_pcap_read;
#endif

  int readsd_count;
  int writesd_count;
#if HAVE_PCAP
  int readpcapsd_count;
#endif

  int watched_events;

  /* The struct npool used to create the iod (used for deletion) */
  struct npool *nsp;

  enum iod_state state;

  /* The host and port we are connected to using sd (saves a call to getpeername) */
  struct sockaddr_storage peer;
  /* The host and port to bind to with sd */
  struct sockaddr_storage local;

  /* The length of peer/local actually used (sizeof(sockaddr_in) or
   * sizeof(sockaddr_in6), SUN_LEN(sockaddr_un), or 0 if peer/local
   * has not been filled in */
  size_t locallen;
  size_t peerlen;

  /* -1 if none yet, otherwise IPPROTO_TCP, etc. */
  int lastproto;

  /* The struct npool keeps track of NIODs that have been allocated so that it
   * can destroy them if the msp is deleted.  This pointer makes it easy to
   * remove this struct niod from the allocated list when necessary */
  gh_lnode_t nodeq;

#define IOD_REGISTERED  0x01
#define IOD_PROCESSED   0x02    /* internally used by engine_kqueue.c */

#define IOD_PROPSET(iod, flag)  ((iod)->_flags |= (flag))
#define IOD_PROPCLR(iod, flag)  ((iod)->_flags &= ~(flag))
#define IOD_PROPGET(iod, flag)  (((iod)->_flags & (flag)) != 0)
  char _flags;

  /* Used for SSL Server Name Indication. */
  char *hostname;

#if HAVE_OPENSSL
  /* An SSL connection (or NULL if none) */
  SSL *ssl;
  /* SSL SESSION ID (or NULL if none) */
  SSL_SESSION *ssl_session;
#else
  /* Because there are many if (nsi->ssl) cases in the code */
  char *ssl;
#endif
  /* Every iod has an id which is always unique for the same nspool (unless you
   * create billions of them) */
  unsigned long id;

  /* No. of bytes read from the sd*/
  unsigned long read_count;
  /* No. of bytes written to the sd */
  unsigned long write_count;

  void *userdata;

  /* IP options to set on socket before connect() */
  void *ipopts;
  int ipoptslen;

  /* Pointer to mspcap struct (used only if pcap support is included) */
  void *pcap;

  struct proxy_chain_context *px_ctx;

};


/* nsock_event_t handles a single event.  Its ID is generally returned when the
 * event is created, and the event is included in callbacks */
struct nevent {
  /* Every event has an ID which is unique for a given nsock unless you blow
   * through more than 500,000,000 events */
  nsock_event_id id;

  enum nse_type type;
  enum nse_status status;

  /* For write events, this is the data to be written, for read events, this is
   * what we will read into */
  struct filespace iobuf;

  /* The timeout of the event -- absolute time
   * except that tv_sec == 0 means no timeout */
  struct timeval timeout;

  /* Info pertaining to READ requests */
  struct readinfo readinfo;
  /* Info pertaining to WRITE requests */
  struct writeinfo writeinfo;

#if HAVE_OPENSSL
  struct sslinfo sslinfo;
#endif

  /* If we return a status of NSE_STATUS_ERROR, this must be set */
  int errnum;

  /* The nsock I/O descriptor related to event (if applicable) */
  struct niod *iod;

  /* The handler to call when event is complete */
  nsock_ev_handler handler;

  /* slot in the expirable binheap */
  gh_hnode_t expire;

  /* For some reasons (see nsock_pcap.c) we register pcap events as both read
   * and pcap_read events when in PCAP_BSD_SELECT_HACK mode. We then need two
   * gh_lnode_t handles. To make code simpler, we _always_ use _nodeq_pcap for
   * pcap_read events and _nodeq_io for the other ones.
   * When not in PCAP_BSD_SELECT_HACK mode we define both handles as members
   * of an union to optimize memory footprint. */
  gh_lnode_t nodeq_io;
  gh_lnode_t nodeq_pcap;

  /* Optional (NULL if unset) pointer to pass to the handler */
  void *userdata;

  /* If this event is all filled out and ready for immediate delivery,
   * event_done is nonzero.  Used when event is finished at unexpected time and
   * we want to dispatch it later to avoid duplicating stat update code and all
   * that other crap */
  unsigned int event_done: 1;
  unsigned int eof: 1;

#if HAVE_IOCP
  struct extended_overlapped *eov;
#endif
};

struct io_operations {
  int(*iod_connect)(struct npool *nsp, int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  int(*iod_read)(struct npool *nsp, int sockfd, void *buf, size_t len, int flags,
    struct sockaddr *src_addr, socklen_t *addrlen);

  int(*iod_write)(struct npool *nsp, int sockfd, const void *buf, size_t len, int flags,
    const struct sockaddr *dest_addr, socklen_t addrlen);
};

struct io_engine {
  /* Human readable identifier for this engine. */
  const char *name;

  /* Engine constructor */
  int (*init)(struct npool *nsp);

  /* Engine destructor */
  void (*destroy)(struct npool *nsp);

  /* Register a new IOD to the engine */
  int(*iod_register)(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev);

  /* Remove a registered IOD */
  int(*iod_unregister)(struct npool *nsp, struct niod *iod);

  /* Modify events for a registered IOD.
   *  - ev_set represent the events to add
   *  - ev_clr represent the events to delete (if set) */
  int (*iod_modify)(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev_set, int ev_clr);

  /* Main engine loop */
  int (*loop)(struct npool *nsp, int msec_timeout);

  /* I/O operations */
  struct io_operations *io_operations;
};

/* ----------- NSOCK I/O ENGINE CONVENIENCE WRAPPERS ------------ */
static inline int nsock_engine_init(struct npool *nsp) {
  return nsp->engine->init(nsp);
}

static inline void nsock_engine_destroy(struct npool *nsp) {
  nsp->engine->destroy(nsp);
  return;
}

static inline int nsock_engine_iod_register(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev) {
  return nsp->engine->iod_register(nsp, iod, nse, ev);
}

static inline int nsock_engine_iod_unregister(struct npool *nsp, struct niod *iod) {
  return nsp->engine->iod_unregister(nsp, iod);
}

static inline int nsock_engine_iod_modify(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev_set, int ev_clr) {
  return nsp->engine->iod_modify(nsp, iod, nse, ev_set, ev_clr);
}

static inline int nsock_engine_loop(struct npool *nsp, int msec_timeout) {
  return nsp->engine->loop(nsp, msec_timeout);
}

/* ------------------- PROTOTYPES ------------------- */

int event_timedout(struct nevent *nse);

/* Get a new nsock_event_id, given a type */
nsock_event_id get_new_event_id(struct npool *nsp, enum nse_type type);

/* Take an event ID and return the type (NSE_TYPE_CONNECT, etc */
enum nse_type get_event_id_type(nsock_event_id event_id);

/* Create a new event structure -- must be deleted later with event_delete,
 * unless it returns NULL (failure).  NULL can be passed in for the struct niod and
 * the userdata if not available. */
struct nevent *event_new(struct npool *nsp, enum nse_type type, struct niod *iod,
                           int timeout_msecs, nsock_ev_handler handler, void *userdata);

/* An internal function for cancelling an event when you already have a pointer
 * to the struct nevent (use nsock_event_cancel if you just have an ID).  The
 * event_list passed in should correspond to the type of the event.  For
 * example, with NSE_TYPE_READ, you would pass in &iod->read_events;.  elem
 * is the list element in event_list which holds the event.  Pass a nonzero for
 * notify if you want the program owning the event to be notified that it has
 * been cancelled */
int nevent_delete(struct npool *nsp, struct nevent *nse, gh_list_t *event_list, gh_lnode_t *elem, int notify);

/* Adjust various statistics, dispatches the event handler (if notify is
 * nonzero) and then deletes the event.  This function does NOT delete the event
 * from any lists it might be on (eg nsp->read_list etc.) nse->event_done
 * MUST be true when you call this */
void event_dispatch_and_delete(struct npool *nsp, struct nevent *nse, int notify);

/* Free an struct nevent which was allocated with event_new, including all internal
 * resources.  Note -- we assume that nse->iod->events_pending (if it exists)
 * has ALREADY been decremented (done during event_dispatch_and_delete) -- so
 * remember to do this if you call event_delete() directly */
void event_delete(struct npool *nsp, struct nevent *nse);

/* Add an event to the appropriate nsp event list, handles housekeeping such as
 * adjusting the descriptor select/poll lists, registering the timeout value,
 * etc. */
void nsock_pool_add_event(struct npool *nsp, struct nevent *nse);

void nsock_connect_internal(struct npool *ms, struct nevent *nse, int type, int proto, struct sockaddr_storage *ss, size_t sslen, unsigned int port);

/* Comments on using the following handle_*_result functions are available in nsock_core.c */

/* handle_connect_results assumes that select or poll have already shown the
 * descriptor to be active */
void handle_connect_result(struct npool *ms, struct nevent *nse, enum nse_status status);

void handle_read_result(struct npool *ms, struct nevent *nse, enum nse_status status);

void handle_write_result(struct npool *ms, struct nevent *nse, enum nse_status status);

void handle_timer_result(struct npool *ms, struct nevent *nse, enum nse_status status);

#if HAVE_PCAP
void handle_pcap_read_result(struct npool *ms, struct nevent *nse, enum nse_status status);
#endif

/* An event has been completed and the handler is about to be called.  This
 * function writes out tracing data about the event if necessary */
void nsock_trace_handler_callback(struct npool *ms, struct nevent *nse);

#if HAVE_OPENSSL
/* Sets the ssl session of an nsock_iod, increments usage count.  The session
 * should not have been set yet (as no freeing is done) */
void nsi_set_ssl_session(struct niod *iod, SSL_SESSION *sessid);
#endif

static inline struct nevent *next_expirable_event(struct npool *nsp) {
  gh_hnode_t *hnode;

  hnode = gh_heap_min(&nsp->expirables);
  if (!hnode)
    return NULL;

  return container_of(hnode, struct nevent, expire);
}

static inline struct nevent *lnode_nevent(gh_lnode_t *lnode) {
  return container_of(lnode, struct nevent, nodeq_io);
}

static inline struct nevent *lnode_nevent2(gh_lnode_t *lnode) {
  return container_of(lnode, struct nevent, nodeq_pcap);
}

#endif /* NSOCK_INTERNAL_H */