9393a972e276f486d631d41bcb00ff0566c195d7
[sfrench/samba-autobuild/.git] / source4 / echo_server / echo_server.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Echo server service example
5
6    Copyright (C) 2010 Kai Blin  <kai@samba.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "echo_server/echo_server.h"
24 /* Get at the config file settings */
25 #include "param/param.h"
26 /* This defines task_server_terminate */
27 #include "smbd/process_model.h"
28 /* We get load_interface_list from here */
29 #include "socket/netif.h"
30 /* NTSTATUS-related stuff */
31 #include "libcli/util/ntstatus.h"
32 /* tsocket-related functions */
33 #include "lib/tsocket/tsocket.h"
34
35 NTSTATUS server_service_echo_init(void);
36
37 /* Structure to hold an echo server socket */
38 struct echo_socket {
39         /* This can come handy for the task struct in there */
40         struct echo_server *echo;
41         struct tsocket_address *local_address;
42 };
43
44 /* Structure to hold udp socket */
45 struct echo_udp_socket {
46         struct echo_socket *echo_socket;
47         struct tdgram_context *dgram;
48         struct tevent_queue *send_queue;
49 };
50
51 /*
52  * Main processing function.
53  *
54  * This is the start of the package processing.
55  * In the echo server it doesn't do much, but for more complicated servers,
56  * your code goes here (or at least is called from here.
57  */
58 static NTSTATUS echo_process(struct echo_server *echo,
59                              TALLOC_CTX *mem_ctx,
60                              DATA_BLOB *in,
61                              DATA_BLOB *out)
62 {
63         uint8_t *buf = talloc_memdup(mem_ctx, in->data, in->length);
64         NT_STATUS_HAVE_NO_MEMORY(buf);
65
66         out->data = buf;
67         out->length = in->length;
68
69         return NT_STATUS_OK;
70 }
71
72 /* Structure keeping track of a single UDP echo server call */
73 struct echo_udp_call {
74         /* The UDP packet came from here, our reply goes there as well */
75         struct tsocket_address *src;
76         DATA_BLOB in;
77         DATA_BLOB out;
78 };
79
80 /** Prototype of the send callback */
81 static void echo_udp_call_sendto_done(struct tevent_req *subreq);
82
83 /* Callback to receive UDP packets */
84 static void echo_udp_call_loop(struct tevent_req *subreq)
85 {
86         /*
87          * Our socket structure is the callback data. Get it in a
88          * type-safe way
89          */
90         struct echo_udp_socket *sock = tevent_req_callback_data(subreq,
91                                        struct echo_udp_socket);
92         struct echo_udp_call *call;
93         uint8_t *buf;
94         ssize_t len;
95         NTSTATUS status;
96         int sys_errno;
97
98         call = talloc(sock, struct echo_udp_call);
99         if (call == NULL) {
100                 goto done;
101         }
102
103         len = tdgram_recvfrom_recv(subreq, &sys_errno, call, &buf, &call->src);
104         TALLOC_FREE(subreq);
105         if (len == -1) {
106                 TALLOC_FREE(call);
107                 goto done;
108         }
109
110         call->in.data = buf;
111         call->in.length = len;
112
113         DEBUG(10, ("Received echo UDP packet of %lu bytes from %s\n",
114                   (long)len, tsocket_address_string(call->src, call)));
115
116         /* Handle the data coming in and compute the reply */
117         status = echo_process(sock->echo_socket->echo, call,
118                               &call->in, &call->out);
119         if (!NT_STATUS_IS_OK(status)) {
120                 TALLOC_FREE(call);
121                 DEBUG(0, ("echo_process returned %s\n",
122                           nt_errstr(status)));
123                 goto done;
124         }
125
126         /* I said the task struct would come in handy. */
127         subreq = tdgram_sendto_queue_send(call,
128                                 sock->echo_socket->echo->task->event_ctx,
129                                 sock->dgram,
130                                 sock->send_queue,
131                                 call->out.data,
132                                 call->out.length,
133                                 call->src);
134         if (subreq == NULL) {
135                 TALLOC_FREE(call);
136                 goto done;
137         }
138
139         tevent_req_set_callback(subreq, echo_udp_call_sendto_done, call);
140
141 done:
142         /* Now loop for the next incoming UDP packet, the async way */
143         subreq = tdgram_recvfrom_send(sock,
144                                 sock->echo_socket->echo->task->event_ctx,
145                                 sock->dgram);
146         if (subreq == NULL) {
147                 task_server_terminate(sock->echo_socket->echo->task,
148                                       "no memory for tdgram_recvfrom_send",
149                                       true);
150                 return;
151         }
152         tevent_req_set_callback(subreq, echo_udp_call_loop, sock);
153 }
154
155 /* Callback to send UDP replies */
156 static void echo_udp_call_sendto_done(struct tevent_req *subreq)
157 {
158         struct echo_udp_call *call = tevent_req_callback_data(subreq,
159                                      struct echo_udp_call);
160         ssize_t ret;
161         int sys_errno;
162
163         ret = tdgram_sendto_queue_recv(subreq, &sys_errno);
164
165         /*
166          * We don't actually care about the error, just get on with our life.
167          * We already set a new echo_udp_call_loop callback already, so we're
168          * almost done, just some memory to free.
169          */
170         TALLOC_FREE(call);
171 }
172
173 /* Start listening on a given address */
174 static NTSTATUS echo_add_socket(struct echo_server *echo,
175                                 const struct model_ops *ops,
176                                 const char *name,
177                                 const char *address,
178                                 uint16_t port)
179 {
180         struct echo_socket *echo_socket;
181         struct echo_udp_socket *echo_udp_socket;
182         struct tevent_req *udpsubreq;
183         NTSTATUS status;
184         int ret;
185
186         echo_socket = talloc(echo, struct echo_socket);
187         NT_STATUS_HAVE_NO_MEMORY(echo_socket);
188
189         echo_socket->echo = echo;
190
191         /*
192          * Initialize the tsocket_address.
193          * The nifty part is the "ip" string. This tells tsocket to autodetect
194          * ipv4 or ipv6 based on the IP address string passed.
195          */
196         ret = tsocket_address_inet_from_strings(echo_socket, "ip",
197                                                 address, port,
198                                                 &echo_socket->local_address);
199         if (ret != 0) {
200                 status = map_nt_error_from_unix(errno);
201                 return status;
202         }
203
204         /* Now set up the udp socket */
205         echo_udp_socket = talloc(echo_socket, struct echo_udp_socket);
206         NT_STATUS_HAVE_NO_MEMORY(echo_udp_socket);
207
208         echo_udp_socket->echo_socket = echo_socket;
209
210         ret = tdgram_inet_udp_socket(echo_socket->local_address,
211                                      NULL,
212                                      echo_udp_socket,
213                                      &echo_udp_socket->dgram);
214         if (ret != 0) {
215                 status = map_nt_error_from_unix(errno);
216                 DEBUG(0, ("Failed to bind to %s:%u UDP - %s\n",
217                           address, port, nt_errstr(status)));
218                 return status;
219         }
220
221         /*
222          * We set up a send queue so we can have multiple UDP packets in flight
223          */
224         echo_udp_socket->send_queue = tevent_queue_create(echo_udp_socket,
225                                                         "echo_udp_send_queue");
226         NT_STATUS_HAVE_NO_MEMORY(echo_udp_socket->send_queue);
227
228         /*
229          * To handle the UDP requests, set up a new tevent request as a
230          * subrequest of the current one.
231          */
232         udpsubreq = tdgram_recvfrom_send(echo_udp_socket,
233                                          echo->task->event_ctx,
234                                          echo_udp_socket->dgram);
235         NT_STATUS_HAVE_NO_MEMORY(udpsubreq);
236         tevent_req_set_callback(udpsubreq, echo_udp_call_loop, echo_udp_socket);
237
238         return NT_STATUS_OK;
239 }
240
241 /* Set up the listening sockets */
242 static NTSTATUS echo_startup_interfaces(struct echo_server *echo,
243                                         struct loadparm_context *lp_ctx,
244                                         struct interface *ifaces)
245 {
246         const struct model_ops *model_ops;
247         int num_interfaces;
248         TALLOC_CTX *tmp_ctx = talloc_new(echo);
249         NTSTATUS status;
250         int i;
251
252         /*
253          * Samba allows subtask to set their own process model.
254          * Available models currently are:
255          * - onefork  (forks exactly one child process)
256          * - prefork  (keep a couple of child processes around)
257          * - single   (only run a single process)
258          * - standard (fork one subprocess per incoming connection)
259          * - thread   (use threads instead of forks)
260          *
261          * For the echo server, the "single" process model works fine,
262          * you probably don't want to use the thread model unless you really
263          * know what you're doing.
264          */
265
266         model_ops = process_model_startup("single");
267         if (model_ops == NULL) {
268                 DEBUG(0, ("Can't find 'single' proces model_ops\n"));
269                 return NT_STATUS_INTERNAL_ERROR;
270         }
271
272         num_interfaces = iface_list_count(ifaces);
273
274         for(i=0; i<num_interfaces; i++) {
275                 const char *address = talloc_strdup(tmp_ctx, iface_list_n_ip(ifaces, i));
276
277                 status = echo_add_socket(echo, model_ops, "echo", address, ECHO_SERVICE_PORT);
278                 NT_STATUS_NOT_OK_RETURN(status);
279         }
280
281         TALLOC_FREE(tmp_ctx);
282         return NT_STATUS_OK;
283 }
284
285
286 /* Do the basic task initialization, check if the task should run */
287
288 static void echo_task_init(struct task_server *task)
289 {
290         struct interface *ifaces;
291         struct echo_server *echo;
292         NTSTATUS status;
293
294         /*
295          * For the purpose of the example, let's only start the server in DC
296          * and standalone modes, and not as a member server.
297          */
298         switch(lpcfg_server_role(task->lp_ctx)) {
299         case ROLE_STANDALONE:
300                 /* Yes, we want to run the echo server */
301                 break;
302         case ROLE_DOMAIN_MEMBER:
303                 task_server_terminate(task, "echo: Not starting echo server " \
304                                       "for domain members", false);
305                 return;
306         case ROLE_DOMAIN_CONTROLLER:
307                 /* Yes, we want to run the echo server */
308                 break;
309         }
310
311         load_interface_list(task, task->lp_ctx, &ifaces);
312
313         if (iface_list_count(ifaces) == 0) {
314                 task_server_terminate(task,
315                                       "echo: No network interfaces configured",
316                                       false);
317                 return;
318         }
319
320         task_server_set_title(task, "task[echo]");
321
322         echo = talloc_zero(task, struct echo_server);
323         if (echo == NULL) {
324                 task_server_terminate(task, "echo: Out of memory", true);
325                 return;
326         }
327
328         echo->task = task;
329
330         status = echo_startup_interfaces(echo, task->lp_ctx, ifaces);
331         if (!NT_STATUS_IS_OK(status)) {
332                 task_server_terminate(task, "echo: Failed to set up interfaces",
333                                       true);
334                 return;
335         }
336 }
337
338 /*
339  * Register this server service with the main samba process.
340  *
341  * This is the function you need to put into the wscript_build file as
342  * init_function. All the real work happens in "echo_task_init" above.
343  */
344 NTSTATUS server_service_echo_init(void)
345 {
346         return register_server_service("echo", echo_task_init);
347 }