spoolss: accept XPS_PASS datatype used by Windows 8
[Samba.git] / source4 / echo_server / echo_server.c
blob02e26bdbe3a7846a9bab071e403b5ac969e4bcbd
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_interfaces 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 /* Structure to hold an echo server socket */
36 struct echo_socket {
37 /* This can come handy for the task struct in there */
38 struct echo_server *echo;
39 struct tsocket_address *local_address;
42 /* Structure to hold udp socket */
43 struct echo_udp_socket {
44 struct echo_socket *echo_socket;
45 struct tdgram_context *dgram;
46 struct tevent_queue *send_queue;
50 * Main processing function.
52 * This is the start of the package processing.
53 * In the echo server it doesn't do much, but for more complicated servers,
54 * your code goes here (or at least is called from here.
56 static NTSTATUS echo_process(struct echo_server *echo,
57 TALLOC_CTX *mem_ctx,
58 DATA_BLOB *in,
59 DATA_BLOB *out)
61 uint8_t *buf = talloc_memdup(mem_ctx, in->data, in->length);
62 NT_STATUS_HAVE_NO_MEMORY(buf);
64 out->data = buf;
65 out->length = in->length;
67 return NT_STATUS_OK;
70 /* Structure keeping track of a single UDP echo server call */
71 struct echo_udp_call {
72 /* The UDP packet came from here, our reply goes there as well */
73 struct tsocket_address *src;
74 DATA_BLOB in;
75 DATA_BLOB out;
78 /** Prototype of the send callback */
79 static void echo_udp_call_sendto_done(struct tevent_req *subreq);
81 /* Callback to receive UDP packets */
82 static void echo_udp_call_loop(struct tevent_req *subreq)
85 * Our socket structure is the callback data. Get it in a
86 * type-safe way
88 struct echo_udp_socket *sock = tevent_req_callback_data(subreq,
89 struct echo_udp_socket);
90 struct echo_udp_call *call;
91 uint8_t *buf;
92 ssize_t len;
93 NTSTATUS status;
94 int sys_errno;
96 call = talloc(sock, struct echo_udp_call);
97 if (call == NULL) {
98 goto done;
101 len = tdgram_recvfrom_recv(subreq, &sys_errno, call, &buf, &call->src);
102 TALLOC_FREE(subreq);
103 if (len == -1) {
104 TALLOC_FREE(call);
105 goto done;
108 call->in.data = buf;
109 call->in.length = len;
111 DEBUG(10, ("Received echo UDP packet of %lu bytes from %s\n",
112 (long)len, tsocket_address_string(call->src, call)));
114 /* Handle the data coming in and compute the reply */
115 status = echo_process(sock->echo_socket->echo, call,
116 &call->in, &call->out);
117 if (!NT_STATUS_IS_OK(status)) {
118 TALLOC_FREE(call);
119 DEBUG(0, ("echo_process returned %s\n",
120 nt_errstr(status)));
121 goto done;
124 /* I said the task struct would come in handy. */
125 subreq = tdgram_sendto_queue_send(call,
126 sock->echo_socket->echo->task->event_ctx,
127 sock->dgram,
128 sock->send_queue,
129 call->out.data,
130 call->out.length,
131 call->src);
132 if (subreq == NULL) {
133 TALLOC_FREE(call);
134 goto done;
137 tevent_req_set_callback(subreq, echo_udp_call_sendto_done, call);
139 done:
140 /* Now loop for the next incoming UDP packet, the async way */
141 subreq = tdgram_recvfrom_send(sock,
142 sock->echo_socket->echo->task->event_ctx,
143 sock->dgram);
144 if (subreq == NULL) {
145 task_server_terminate(sock->echo_socket->echo->task,
146 "no memory for tdgram_recvfrom_send",
147 true);
148 return;
150 tevent_req_set_callback(subreq, echo_udp_call_loop, sock);
153 /* Callback to send UDP replies */
154 static void echo_udp_call_sendto_done(struct tevent_req *subreq)
156 struct echo_udp_call *call = tevent_req_callback_data(subreq,
157 struct echo_udp_call);
158 ssize_t ret;
159 int sys_errno;
161 ret = tdgram_sendto_queue_recv(subreq, &sys_errno);
164 * We don't actually care about the error, just get on with our life.
165 * We already set a new echo_udp_call_loop callback already, so we're
166 * almost done, just some memory to free.
168 TALLOC_FREE(call);
171 /* Start listening on a given address */
172 static NTSTATUS echo_add_socket(struct echo_server *echo,
173 const struct model_ops *ops,
174 const char *name,
175 const char *address,
176 uint16_t port)
178 struct echo_socket *echo_socket;
179 struct echo_udp_socket *echo_udp_socket;
180 struct tevent_req *udpsubreq;
181 NTSTATUS status;
182 int ret;
184 echo_socket = talloc(echo, struct echo_socket);
185 NT_STATUS_HAVE_NO_MEMORY(echo_socket);
187 echo_socket->echo = echo;
190 * Initialize the tsocket_address.
191 * The nifty part is the "ip" string. This tells tsocket to autodetect
192 * ipv4 or ipv6 based on the IP address string passed.
194 ret = tsocket_address_inet_from_strings(echo_socket, "ip",
195 address, port,
196 &echo_socket->local_address);
197 if (ret != 0) {
198 status = map_nt_error_from_unix(errno);
199 return status;
202 /* Now set up the udp socket */
203 echo_udp_socket = talloc(echo_socket, struct echo_udp_socket);
204 NT_STATUS_HAVE_NO_MEMORY(echo_udp_socket);
206 echo_udp_socket->echo_socket = echo_socket;
208 ret = tdgram_inet_udp_socket(echo_socket->local_address,
209 NULL,
210 echo_udp_socket,
211 &echo_udp_socket->dgram);
212 if (ret != 0) {
213 status = map_nt_error_from_unix(errno);
214 DEBUG(0, ("Failed to bind to %s:%u UDP - %s\n",
215 address, port, nt_errstr(status)));
216 return status;
220 * We set up a send queue so we can have multiple UDP packets in flight
222 echo_udp_socket->send_queue = tevent_queue_create(echo_udp_socket,
223 "echo_udp_send_queue");
224 NT_STATUS_HAVE_NO_MEMORY(echo_udp_socket->send_queue);
227 * To handle the UDP requests, set up a new tevent request as a
228 * subrequest of the current one.
230 udpsubreq = tdgram_recvfrom_send(echo_udp_socket,
231 echo->task->event_ctx,
232 echo_udp_socket->dgram);
233 NT_STATUS_HAVE_NO_MEMORY(udpsubreq);
234 tevent_req_set_callback(udpsubreq, echo_udp_call_loop, echo_udp_socket);
236 return NT_STATUS_OK;
239 /* Set up the listening sockets */
240 static NTSTATUS echo_startup_interfaces(struct echo_server *echo,
241 struct loadparm_context *lp_ctx,
242 struct interface *ifaces)
244 const struct model_ops *model_ops;
245 int num_interfaces;
246 TALLOC_CTX *tmp_ctx = talloc_new(echo);
247 NTSTATUS status;
248 int i;
251 * Samba allows subtask to set their own process model.
252 * Available models currently are:
253 * - onefork (forks exactly one child process)
254 * - prefork (keep a couple of child processes around)
255 * - single (only run a single process)
256 * - standard (fork one subprocess per incoming connection)
257 * - thread (use threads instead of forks)
259 * For the echo server, the "single" process model works fine,
260 * you probably don't want to use the thread model unless you really
261 * know what you're doing.
264 model_ops = process_model_startup("single");
265 if (model_ops == NULL) {
266 DEBUG(0, ("Can't find 'single' proces model_ops\n"));
267 return NT_STATUS_INTERNAL_ERROR;
270 num_interfaces = iface_count(ifaces);
272 for(i=0; i<num_interfaces; i++) {
273 const char *address = talloc_strdup(tmp_ctx, iface_n_ip(ifaces, i));
275 status = echo_add_socket(echo, model_ops, "echo", address, ECHO_SERVICE_PORT);
276 NT_STATUS_NOT_OK_RETURN(status);
279 TALLOC_FREE(tmp_ctx);
280 return NT_STATUS_OK;
284 /* Do the basic task initialization, check if the task should run */
286 static void echo_task_init(struct task_server *task)
288 struct interface *ifaces;
289 struct echo_server *echo;
290 NTSTATUS status;
293 * For the purpose of the example, let's only start the server in DC
294 * and standalone modes, and not as a member server.
296 switch(lpcfg_server_role(task->lp_ctx)) {
297 case ROLE_STANDALONE:
298 /* Yes, we want to run the echo server */
299 break;
300 case ROLE_DOMAIN_MEMBER:
301 task_server_terminate(task, "echo: Not starting echo server " \
302 "for domain members", false);
303 return;
304 case ROLE_DOMAIN_CONTROLLER:
305 /* Yes, we want to run the echo server */
306 break;
309 load_interfaces(task, lpcfg_interfaces(task->lp_ctx), &ifaces);
311 if (iface_count(ifaces) == 0) {
312 task_server_terminate(task,
313 "echo: No network interfaces configured",
314 false);
315 return;
318 task_server_set_title(task, "task[echo]");
320 echo = talloc_zero(task, struct echo_server);
321 if (echo == NULL) {
322 task_server_terminate(task, "echo: Out of memory", true);
323 return;
326 echo->task = task;
328 status = echo_startup_interfaces(echo, task->lp_ctx, ifaces);
329 if (!NT_STATUS_IS_OK(status)) {
330 task_server_terminate(task, "echo: Failed to set up interfaces",
331 true);
332 return;
337 * Register this server service with the main samba process.
339 * This is the function you need to put into the wscript_build file as
340 * init_function. All the real work happens in "echo_task_init" above.
342 NTSTATUS server_service_echo_init(void)
344 return register_server_service("echo", echo_task_init);