153c49d34c3166f1b2f48a590d6faaf137b90b80
[tprouty/samba.git] / source / lib / packet.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Packet handling
4    Copyright (C) Volker Lendecke 2007
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 3 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., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 struct packet_context {
24         int fd;
25         struct data_blob in, out;
26 };
27
28 /*
29  * Close the underlying fd
30  */
31 static int packet_context_destructor(struct packet_context *ctx)
32 {
33         return close(ctx->fd);
34 }
35
36 /*
37  * Initialize a packet context. The fd is given to the packet context, meaning
38  * that it is automatically closed when the packet context is freed.
39  */
40 struct packet_context *packet_init(TALLOC_CTX *mem_ctx, int fd)
41 {
42         struct packet_context *result;
43
44         if (!(result = TALLOC_ZERO_P(mem_ctx, struct packet_context))) {
45                 return NULL;
46         }
47
48         result->fd = fd;
49         talloc_set_destructor(result, packet_context_destructor);
50         return result;
51 }
52
53 /*
54  * Pull data from the fd
55  */
56 NTSTATUS packet_fd_read(struct packet_context *ctx)
57 {
58         int res, available;
59         size_t new_size;
60         uint8 *in;
61
62         res = ioctl(ctx->fd, FIONREAD, &available);
63
64         if (res == -1) {
65                 DEBUG(10, ("ioctl(FIONREAD) failed: %s\n", strerror(errno)));
66                 return map_nt_error_from_unix(errno);
67         }
68
69         SMB_ASSERT(available >= 0);
70
71         if (available == 0) {
72                 return NT_STATUS_END_OF_FILE;
73         }
74
75         new_size = ctx->in.length + available;
76
77         if (new_size < ctx->in.length) {
78                 DEBUG(0, ("integer wrap\n"));
79                 return NT_STATUS_NO_MEMORY;
80         }
81
82         if (!(in = TALLOC_REALLOC_ARRAY(ctx, ctx->in.data, uint8, new_size))) {
83                 DEBUG(10, ("talloc failed\n"));
84                 return NT_STATUS_NO_MEMORY;
85         }
86
87         res = recv(ctx->fd, in + ctx->in.length, available, 0);
88
89         if (res < 0) {
90                 DEBUG(10, ("recv failed: %s\n", strerror(errno)));
91                 return map_nt_error_from_unix(errno);
92         }
93
94         if (res == 0) {
95                 return NT_STATUS_END_OF_FILE;
96         }
97
98         ctx->in.data = in;
99         ctx->in.length += available;
100
101         return NT_STATUS_OK;
102 }
103
104 NTSTATUS packet_fd_read_sync(struct packet_context *ctx)
105 {
106         int res;
107         fd_set r_fds;
108
109         FD_ZERO(&r_fds);
110         FD_SET(ctx->fd, &r_fds);
111
112         res = sys_select(ctx->fd+1, &r_fds, NULL, NULL, NULL);
113
114         if (res == -1) {
115                 DEBUG(10, ("select returned %s\n", strerror(errno)));
116                 return map_nt_error_from_unix(errno);
117         }
118
119         return packet_fd_read(ctx);
120 }
121
122 BOOL packet_handler(struct packet_context *ctx,
123                     BOOL (*full_req)(const struct data_blob *data,
124                                      size_t *length,
125                                      void *private_data),
126                     NTSTATUS (*callback)(const struct data_blob *data,
127                                          void *private_data),
128                     void *private_data,
129                     NTSTATUS *status)
130 {
131         size_t length;
132         struct data_blob data;
133
134         if (!full_req(&ctx->in, &length, private_data)) {
135                 return False;
136         }
137
138         SMB_ASSERT(length <= ctx->in.length);
139
140         data.data = ctx->in.data;
141         data.length = length;
142
143         *status = callback(&data, private_data);
144
145         memmove(ctx->in.data, ctx->in.data + length,
146                 ctx->in.length - length);
147         ctx->in.length -= length;
148
149         return True;
150 }
151
152 /*
153  * How many bytes of outgoing data do we have pending?
154  */
155 size_t packet_outgoing_bytes(struct packet_context *ctx)
156 {
157         return ctx->out.length;
158 }
159
160 /*
161  * Push data to the fd
162  */
163 NTSTATUS packet_fd_write(struct packet_context *ctx)
164 {
165         ssize_t sent;
166
167         sent = send(ctx->fd, ctx->out.data, ctx->out.length, 0);
168
169         if (sent == -1) {
170                 DEBUG(0, ("send failed: %s\n", strerror(errno)));
171                 return map_nt_error_from_unix(errno);
172         }
173
174         memmove(ctx->out.data, ctx->out.data + sent,
175                 ctx->out.length - sent);
176         ctx->out.length -= sent;
177
178         return NT_STATUS_OK;
179 }
180
181 /*
182  * Sync flush all outgoing bytes
183  */
184 NTSTATUS packet_flush(struct packet_context *ctx)
185 {
186         while (ctx->out.length != 0) {
187                 NTSTATUS status = packet_fd_write(ctx);
188                 if (!NT_STATUS_IS_OK(status)) {
189                         return status;
190                 }
191         }
192         return NT_STATUS_OK;
193 }
194
195 /*
196  * Send a list of DATA_BLOBs
197  *
198  * Example:  packet_send(ctx, 2, data_blob_const(&size, sizeof(size)),
199  *                       data_blob_const(buf, size));
200  */
201 NTSTATUS packet_send(struct packet_context *ctx, int num_blobs, ...)
202 {
203         va_list ap;
204         int i;
205         size_t len;
206         uint8 *out;
207
208         len = ctx->out.length;
209
210         va_start(ap, num_blobs);
211         for (i=0; i<num_blobs; i++) {
212                 size_t tmp;
213                 struct data_blob blob = va_arg(ap, struct data_blob);
214
215                 tmp = len + blob.length;
216                 if (tmp < len) {
217                         DEBUG(0, ("integer overflow\n"));
218                         va_end(ap);
219                         return NT_STATUS_NO_MEMORY;
220                 }
221                 len = tmp;
222         }
223         va_end(ap);
224
225         if (len == 0) {
226                 return NT_STATUS_OK;
227         }
228
229         if (!(out = TALLOC_REALLOC_ARRAY(ctx, ctx->out.data, uint8, len))) {
230                 DEBUG(0, ("talloc failed\n"));
231                 return NT_STATUS_NO_MEMORY;
232         }
233
234         ctx->out.data = out;
235
236         va_start(ap, num_blobs);
237         for (i=0; i<num_blobs; i++) {
238                 struct data_blob blob = va_arg(ap, struct data_blob);
239
240                 memcpy(ctx->out.data+ctx->out.length, blob.data, blob.length);
241                 ctx->out.length += blob.length;
242         }
243         va_end(ap);
244
245         SMB_ASSERT(ctx->out.length == len);
246         return NT_STATUS_OK;
247 }
248
249 /*
250  * Get the packet context's file descriptor
251  */
252 int packet_get_fd(struct packet_context *ctx)
253 {
254         return ctx->fd;
255 }
256