2 Unix SMB/CIFS implementation.
4 Echo server service example
6 Copyright (C) 2010 Kai Blin <kai@samba.org>
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.
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.
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/>.
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"
35 NTSTATUS
server_service_echo_init(void);
37 /* Structure to hold an echo server socket */
39 /* This can come handy for the task struct in there */
40 struct echo_server
*echo
;
41 struct tsocket_address
*local_address
;
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
;
52 * Main processing function.
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.
58 static NTSTATUS
echo_process(struct echo_server
*echo
,
63 uint8_t *buf
= talloc_memdup(mem_ctx
, in
->data
, in
->length
);
64 NT_STATUS_HAVE_NO_MEMORY(buf
);
67 out
->length
= in
->length
;
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
;
80 /** Prototype of the send callback */
81 static void echo_udp_call_sendto_done(struct tevent_req
*subreq
);
83 /* Callback to receive UDP packets */
84 static void echo_udp_call_loop(struct tevent_req
*subreq
)
87 * Our socket structure is the callback data. Get it in a
90 struct echo_udp_socket
*sock
= tevent_req_callback_data(subreq
,
91 struct echo_udp_socket
);
92 struct echo_udp_call
*call
;
98 call
= talloc(sock
, struct echo_udp_call
);
103 len
= tdgram_recvfrom_recv(subreq
, &sys_errno
, call
, &buf
, &call
->src
);
111 call
->in
.length
= len
;
113 DEBUG(10, ("Received echo UDP packet of %lu bytes from %s\n",
114 (long)len
, tsocket_address_string(call
->src
, call
)));
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
)) {
121 DEBUG(0, ("echo_process returned %s\n",
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
,
134 if (subreq
== NULL
) {
139 tevent_req_set_callback(subreq
, echo_udp_call_sendto_done
, call
);
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
,
146 if (subreq
== NULL
) {
147 task_server_terminate(sock
->echo_socket
->echo
->task
,
148 "no memory for tdgram_recvfrom_send",
152 tevent_req_set_callback(subreq
, echo_udp_call_loop
, sock
);
155 /* Callback to send UDP replies */
156 static void echo_udp_call_sendto_done(struct tevent_req
*subreq
)
158 struct echo_udp_call
*call
= tevent_req_callback_data(subreq
,
159 struct echo_udp_call
);
163 ret
= tdgram_sendto_queue_recv(subreq
, &sys_errno
);
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.
173 /* Start listening on a given address */
174 static NTSTATUS
echo_add_socket(struct echo_server
*echo
,
175 const struct model_ops
*ops
,
180 struct echo_socket
*echo_socket
;
181 struct echo_udp_socket
*echo_udp_socket
;
182 struct tevent_req
*udpsubreq
;
186 echo_socket
= talloc(echo
, struct echo_socket
);
187 NT_STATUS_HAVE_NO_MEMORY(echo_socket
);
189 echo_socket
->echo
= echo
;
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.
196 ret
= tsocket_address_inet_from_strings(echo_socket
, "ip",
198 &echo_socket
->local_address
);
200 status
= map_nt_error_from_unix_common(errno
);
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
);
208 echo_udp_socket
->echo_socket
= echo_socket
;
210 ret
= tdgram_inet_udp_socket(echo_socket
->local_address
,
213 &echo_udp_socket
->dgram
);
215 status
= map_nt_error_from_unix_common(errno
);
216 DEBUG(0, ("Failed to bind to %s:%u UDP - %s\n",
217 address
, port
, nt_errstr(status
)));
222 * We set up a send queue so we can have multiple UDP packets in flight
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
);
229 * To handle the UDP requests, set up a new tevent request as a
230 * subrequest of the current one.
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
);
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
)
246 const struct model_ops
*model_ops
;
248 TALLOC_CTX
*tmp_ctx
= talloc_new(echo
);
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)
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.
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
;
272 num_interfaces
= iface_list_count(ifaces
);
274 for(i
=0; i
<num_interfaces
; i
++) {
275 const char *address
= talloc_strdup(tmp_ctx
, iface_list_n_ip(ifaces
, i
));
277 status
= echo_add_socket(echo
, model_ops
, "echo", address
, ECHO_SERVICE_PORT
);
278 NT_STATUS_NOT_OK_RETURN(status
);
281 TALLOC_FREE(tmp_ctx
);
286 /* Do the basic task initialization, check if the task should run */
288 static void echo_task_init(struct task_server
*task
)
290 struct interface
*ifaces
;
291 struct echo_server
*echo
;
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.
298 switch(lpcfg_server_role(task
->lp_ctx
)) {
299 case ROLE_STANDALONE
:
300 /* Yes, we want to run the echo server */
302 case ROLE_DOMAIN_MEMBER
:
303 task_server_terminate(task
, "echo: Not starting echo server " \
304 "for domain members", false);
306 case ROLE_ACTIVE_DIRECTORY_DC
:
307 /* Yes, we want to run the echo server */
311 load_interface_list(task
, task
->lp_ctx
, &ifaces
);
313 if (iface_list_count(ifaces
) == 0) {
314 task_server_terminate(task
,
315 "echo: No network interfaces configured",
320 task_server_set_title(task
, "task[echo]");
322 echo
= talloc_zero(task
, struct echo_server
);
324 task_server_terminate(task
, "echo: Out of memory", true);
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",
339 * Register this server service with the main samba process.
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.
344 NTSTATUS
server_service_echo_init(void)
346 return register_server_service("echo", echo_task_init
);