summaryrefslogtreecommitdiff
path: root/usr.sbin/nsd/xfrd-tcp.h
blob: ab20cf7bf24b733e58a67e7ee0cb56e40ec8cde1 (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
/*
 * xfrd-tcp.h - XFR (transfer) Daemon TCP system header file. Manages tcp conn.
 *
 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
 *
 * See LICENSE for the license.
 *
 */

#ifndef XFRD_TCP_H
#define XFRD_TCP_H

#include "xfrd.h"
#ifdef HAVE_TLS_1_3
#include <openssl/ssl.h>
#endif


struct buffer;
struct xfrd_zone;
struct xfrd_soa;
struct xfrd_state;
struct region;
struct dname;
struct acl_options;

struct xfrd_tcp_pipeline;
typedef struct xfrd_tcp xfrd_tcp_type;
typedef struct xfrd_tcp_set xfrd_tcp_set_type;
/*
 * A set of xfrd tcp connections.
 */
struct xfrd_tcp_set {
	/* tcp connections, array, each has packet and read/wr state */
	struct xfrd_tcp_pipeline **tcp_state;
	/* max number of tcp connections, size of tcp_state array */
	int tcp_max;
	/* max number of simultaneous connections on a tcp_pipeline */
	int tcp_pipeline;
	/* number of TCP connections in use. */
	int tcp_count;
	/* TCP timeout. */
	int tcp_timeout;
	/* rbtree with pipelines sorted by master */
	rbtree_type* pipetree;
#ifdef HAVE_TLS_1_3
	/* XoT: SSL context */
	SSL_CTX* ssl_ctx;
#endif
	/* double linked list of zones waiting for a TCP connection */
	struct xfrd_zone *tcp_waiting_first, *tcp_waiting_last;
};

/*
 * Structure to keep track of an open tcp connection
 * The xfrd tcp connection is used to first make a request
 * Then to receive the answer packet(s).
 */
struct xfrd_tcp {
	/* tcp connection state */
	/* state: reading or writing */
	uint8_t is_reading;

	/* how many bytes have been read/written - total,
	   incl. tcp length bytes */
	uint32_t total_bytes;

	/* msg len bytes */
	uint16_t msglen;

	/* fd of connection. -1 means unconnected */
	int fd;

	/* packet buffer of connection */
	struct buffer* packet;
};

/* use illegal pointer value to denote skipped ID number.
 * if this does not work, we can allocate with malloc */
#define TCP_NULL_SKIP ((struct xfrd_zone*)-1)

/**
 * The per-id zone pointers, with TCP_NULL_SKIP or a zone pointer for the
 * ID value.
 */
struct xfrd_tcp_pipeline_id {
	/** rbtree node as first member, this is the key. */
	rbnode_type node;
	/** the ID of this member */
	uint16_t id;
	/** zone pointer or TCP_NULL_SKIP */
	struct xfrd_zone* zone;
	/** next free in free list */
	struct xfrd_tcp_pipeline_id* next_free;
};

/**
 * The tcp pipeline key structure. By ip_len, ip, num_unused and unique by
 * pointer value.
 */
struct xfrd_tcp_pipeline_key {
	/* the rbtree node, sorted by IP and nr of unused queries */
	rbnode_type node;
	/* destination IP address */
#ifdef INET6
	struct sockaddr_storage ip;
#else
	struct sockaddr_in ip;
#endif /* INET6 */
	socklen_t ip_len;
	/* number of unused IDs.  used IDs are waiting to send their query,
	 * or have been sent but not not all answer packets have been received.
	 * Sorted by num_unused, so a lookup smaller-equal for 65536 finds the
	 * connection to that master that has the most free IDs. */
	int num_unused;
	/* number of skip-set IDs (these are 'in-use') */
	int num_skip;
};

/**
 * Structure to keep track of a pipelined set of queries on
 * an open tcp connection.  The queries may be answered with
 * interleaved answer packets, the ID number disambiguates.
 * Sorted by the master IP address so you can use lookup with
 * smaller-or-equal to find the tcp connection most suitable.
 */
struct xfrd_tcp_pipeline {
	/* the key information for the tcp pipeline, in its own
	 * struct so it can be referenced on its own for comparison funcs */
	struct xfrd_tcp_pipeline_key key;

	int handler_added;
	/* the event handler for this pipe (it'll disambiguate by ID) */
	struct event handler;

	/* the tcp connection to use for reading */
	struct xfrd_tcp* tcp_r;
	/* the tcp connection to use for writing, if it is done successfully,
	 * then the first zone from the sendlist can be removed. */
	struct xfrd_tcp* tcp_w;
	/* once a byte has been written, handshake complete */
	int connection_established;
#ifdef HAVE_TLS_1_3
	/* XoT: SSL object */
	SSL *ssl;
	/* XoT: if SSL handshake is not done, handshake_want indicates the
	 * last error. This may be SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
	 * when the handshake is still in progress.
	 */
	int  handshake_want;
	/* XoT: 1 if the SSL handshake has succeeded, 0 otherwise */
	int  handshake_done;
#endif

	/* list of queries that want to send, first to get write event,
	 * if NULL, no write event interest */
	struct xfrd_zone* tcp_send_first, *tcp_send_last;

	/* size of the id and unused arrays. */
	int pipe_num;
	/* list of free xfrd_tcp_pipeline_id nodes, these are not in the
	 * zone_per_id tree. preallocated at pipe_num amount. */
	struct xfrd_tcp_pipeline_id* pipe_id_free_list;
	/* The xfrd_zone pointers, per id number.
	 * The key is struct xfrd_tcp_pipeline_id.
	 * per-ID number the queries that have this ID number, every
	 * query owns one ID numbers (until it is done). NULL: unused
	 * When a query is done but not all answer-packets have been
	 * consumed for that ID number, the rest is skipped, this
	 * is denoted with the pointer-value TCP_NULL_SKIP, the ids that
	 * are skipped are not on the unused list.  They may be
	 * removed once the last answer packet is skipped.
	 * pipe_num-num_unused values are in the tree (either
	 * a zone pointer or SKIP) */
	rbtree_type* zone_per_id;
	/* Array of uint16_t, with ID values.
	 * unused ID numbers; the first part of the array contains the IDs */
	uint16_t* unused;
};

/* create set of tcp connections */
struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, const char *tls_cert_bundle, int tcp_max, int tcp_pipeline);

/* init tcp state */
struct xfrd_tcp* xfrd_tcp_create(struct region* region, size_t bufsize);
/* obtain tcp connection for a zone (or wait) */
void xfrd_tcp_obtain(struct xfrd_tcp_set* set, struct xfrd_zone* zone);
/* release tcp connection for a zone (starts waiting) */
void xfrd_tcp_release(struct xfrd_tcp_set* set, struct xfrd_zone* zone);
/* release tcp pipe entirely (does not stop the zones inside it) */
void xfrd_tcp_pipe_release(struct xfrd_tcp_set* set,
	struct xfrd_tcp_pipeline* tp, int conn);
/* use tcp connection to start xfr */
void xfrd_tcp_setup_write_packet(struct xfrd_tcp_pipeline* tp,
	struct xfrd_zone* zone);
/* initialize tcp_state for a zone. Opens the connection. true on success.*/
int xfrd_tcp_open(struct xfrd_tcp_set* set, struct xfrd_tcp_pipeline* tp,
	struct xfrd_zone* zone);
/* read data from tcp, maybe partial read */
void xfrd_tcp_read(struct xfrd_tcp_pipeline* tp);
/* write data to tcp, maybe a partial write */
void xfrd_tcp_write(struct xfrd_tcp_pipeline* tp, struct xfrd_zone* zone);
/* handle tcp pipe events */
void xfrd_handle_tcp_pipe(int fd, short event, void* arg);

/*
 * Read from a stream connection (size16)+packet into buffer.
 * returns value is
 *	-1 on error.
 *	0 on short read, call back later.
 *	1 on completed read.
 * On first call, make sure total_bytes = 0, msglen=0, buffer_clear().
 * and the packet and fd need to be set.
 */
int conn_read(struct xfrd_tcp* conn);
/*
 * Write to a stream connection (size16)+packet.
 * return value is
 * -1 on error. 0 on short write, call back later. 1 completed write.
 * On first call, make sure total_bytes=0, msglen=buffer_limit(),
 * buffer_flipped(). packet and fd need to be set.
 */
int conn_write(struct xfrd_tcp* conn);

/* setup DNS packet for a query of this type */
void xfrd_setup_packet(struct buffer* packet,
        uint16_t type, uint16_t klass, const struct dname* dname, uint16_t qid);
/* write soa in network format to the packet buffer */
void xfrd_write_soa_buffer(struct buffer* packet,
        const struct dname* apex, struct xfrd_soa* soa);
/* use acl address to setup sockaddr struct, returns length of addr. */
socklen_t xfrd_acl_sockaddr_to(struct acl_options* acl,
#ifdef INET6
	struct sockaddr_storage *to);
#else
	struct sockaddr_in *to);
#endif /* INET6 */

socklen_t xfrd_acl_sockaddr_frm(struct acl_options* acl,
#ifdef INET6
	struct sockaddr_storage *frm);
#else
	struct sockaddr_in *frm);
#endif /* INET6 */

/* create pipeline tcp structure */
struct xfrd_tcp_pipeline* xfrd_tcp_pipeline_create(region_type* region,
	int tcp_pipeline);
/* pick num uint16_t values, from 0..max-1, store in array */
void pick_id_values(uint16_t* array, int num, int max);

#endif /* XFRD_TCP_H */