Revert "smbd: add simple noop smbd_impersonate_{conn_vuid,conn_sess,root,guest}_creat...
[Samba.git] / source4 / echo_server / echo_server.c
blobf38999ae13940dd10c93c1884411f8a331763f5d
1 /*
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/>.
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 #include "libds/common/roles.h"
36 NTSTATUS server_service_echo_init(TALLOC_CTX *);
38 /* Structure to hold an echo server socket */
39 struct echo_socket {
40 /* This can come handy for the task struct in there */
41 struct echo_server *echo;
42 struct tsocket_address *local_address;
45 /* Structure to hold udp socket */
46 struct echo_udp_socket {
47 struct echo_socket *echo_socket;
48 struct tdgram_context *dgram;
49 struct tevent_queue *send_queue;
53 * Main processing function.
55 * This is the start of the package processing.
56 * In the echo server it doesn't do much, but for more complicated servers,
57 * your code goes here (or at least is called from here.
59 static NTSTATUS echo_process(struct echo_server *echo,
60 TALLOC_CTX *mem_ctx,
61 DATA_BLOB *in,
62 DATA_BLOB *out)
64 uint8_t *buf = talloc_memdup(mem_ctx, in->data, in->length);
65 NT_STATUS_HAVE_NO_MEMORY(buf);
67 out->data = buf;
68 out->length = in->length;
70 return NT_STATUS_OK;
73 /* Structure keeping track of a single UDP echo server call */
74 struct echo_udp_call {
75 /* The UDP packet came from here, our reply goes there as well */
76 struct tsocket_address *src;
77 DATA_BLOB in;
78 DATA_BLOB out;
81 /** Prototype of the send callback */
82 static void echo_udp_call_sendto_done(struct tevent_req *subreq);
84 /* Callback to receive UDP packets */
85 static void echo_udp_call_loop(struct tevent_req *subreq)
88 * Our socket structure is the callback data. Get it in a
89 * type-safe way
91 struct echo_udp_socket *sock = tevent_req_callback_data(subreq,
92 struct echo_udp_socket);
93 struct echo_udp_call *call;
94 uint8_t *buf;
95 ssize_t len;
96 NTSTATUS status;
97 int sys_errno;
99 call = talloc(sock, struct echo_udp_call);
100 if (call == NULL) {
101 goto done;
104 len = tdgram_recvfrom_recv(subreq, &sys_errno, call, &buf, &call->src);
105 TALLOC_FREE(subreq);
106 if (len == -1) {
107 TALLOC_FREE(call);
108 goto done;
111 call->in.data = buf;
112 call->in.length = len;
114 DEBUG(10, ("Received echo UDP packet of %lu bytes from %s\n",
115 (long)len, tsocket_address_string(call->src, call)));
117 /* Handle the data coming in and compute the reply */
118 status = echo_process(sock->echo_socket->echo, call,
119 &call->in, &call->out);
120 if (!NT_STATUS_IS_OK(status)) {
121 TALLOC_FREE(call);
122 DEBUG(0, ("echo_process returned %s\n",
123 nt_errstr(status)));
124 goto done;
127 /* I said the task struct would come in handy. */
128 subreq = tdgram_sendto_queue_send(call,
129 sock->echo_socket->echo->task->event_ctx,
130 sock->dgram,
131 sock->send_queue,
132 call->out.data,
133 call->out.length,
134 call->src);
135 if (subreq == NULL) {
136 TALLOC_FREE(call);
137 goto done;
140 tevent_req_set_callback(subreq, echo_udp_call_sendto_done, call);
142 done:
143 /* Now loop for the next incoming UDP packet, the async way */
144 subreq = tdgram_recvfrom_send(sock,
145 sock->echo_socket->echo->task->event_ctx,
146 sock->dgram);
147 if (subreq == NULL) {
148 task_server_terminate(sock->echo_socket->echo->task,
149 "no memory for tdgram_recvfrom_send",
150 true);
151 return;
153 tevent_req_set_callback(subreq, echo_udp_call_loop, sock);
156 /* Callback to send UDP replies */
157 static void echo_udp_call_sendto_done(struct tevent_req *subreq)
159 struct echo_udp_call *call = tevent_req_callback_data(subreq,
160 struct echo_udp_call);
161 int sys_errno;
163 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.
170 TALLOC_FREE(call);
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)
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;
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",
197 address, port,
198 &echo_socket->local_address);
199 if (ret != 0) {
200 status = map_nt_error_from_unix_common(errno);
201 return status;
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,
211 NULL,
212 echo_udp_socket,
213 &echo_udp_socket->dgram);
214 if (ret != 0) {
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)));
218 return 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);
238 return NT_STATUS_OK;
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 const struct model_ops *model_ops)
247 int num_interfaces;
248 TALLOC_CTX *tmp_ctx = talloc_new(echo);
249 NTSTATUS status;
250 int i;
252 num_interfaces = iface_list_count(ifaces);
254 for(i=0; i<num_interfaces; i++) {
255 const char *address = talloc_strdup(tmp_ctx, iface_list_n_ip(ifaces, i));
257 status = echo_add_socket(echo, model_ops, "echo", address, ECHO_SERVICE_PORT);
258 NT_STATUS_NOT_OK_RETURN(status);
261 TALLOC_FREE(tmp_ctx);
262 return NT_STATUS_OK;
266 /* Do the basic task initialization, check if the task should run */
268 static NTSTATUS echo_task_init(struct task_server *task)
270 struct interface *ifaces;
271 struct echo_server *echo;
272 NTSTATUS status;
275 * For the purpose of the example, let's only start the server in DC
276 * and standalone modes, and not as a member server.
278 switch(lpcfg_server_role(task->lp_ctx)) {
279 case ROLE_STANDALONE:
280 /* Yes, we want to run the echo server */
281 break;
282 case ROLE_DOMAIN_MEMBER:
283 task_server_terminate(task, "echo: Not starting echo server " \
284 "for domain members", false);
285 return NT_STATUS_INVALID_DOMAIN_ROLE;
286 case ROLE_ACTIVE_DIRECTORY_DC:
287 /* Yes, we want to run the echo server */
288 break;
291 load_interface_list(task, task->lp_ctx, &ifaces);
293 if (iface_list_count(ifaces) == 0) {
294 task_server_terminate(task,
295 "echo: No network interfaces configured",
296 false);
297 return NT_STATUS_UNSUCCESSFUL;
300 task_server_set_title(task, "task[echo]");
302 echo = talloc_zero(task, struct echo_server);
303 if (echo == NULL) {
304 task_server_terminate(task, "echo: Out of memory", true);
305 return NT_STATUS_NO_MEMORY;
308 echo->task = task;
310 status = echo_startup_interfaces(echo, task->lp_ctx, ifaces,
311 task->model_ops);
312 if (!NT_STATUS_IS_OK(status)) {
313 task_server_terminate(task, "echo: Failed to set up interfaces",
314 true);
315 return status;
317 return NT_STATUS_OK;
321 * Register this server service with the main samba process.
323 * This is the function you need to put into the wscript_build file as
324 * init_function. All the real work happens in "echo_task_init" above.
326 NTSTATUS server_service_echo_init(TALLOC_CTX *ctx)
328 static const struct service_details details = {
329 .inhibit_fork_on_accept = true,
330 .inhibit_pre_fork = true,
331 .task_init = echo_task_init,
332 .post_fork = NULL
335 return register_server_service(ctx, "echo", &details);