Line data Source code
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 "samba/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"
35 :
36 : NTSTATUS server_service_echo_init(TALLOC_CTX *);
37 :
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;
43 : };
44 :
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;
50 : };
51 :
52 : /*
53 : * Main processing function.
54 : *
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.
58 : */
59 1 : static NTSTATUS echo_process(struct echo_server *echo,
60 : TALLOC_CTX *mem_ctx,
61 : DATA_BLOB *in,
62 : DATA_BLOB *out)
63 : {
64 1 : uint8_t *buf = talloc_memdup(mem_ctx, in->data, in->length);
65 1 : NT_STATUS_HAVE_NO_MEMORY(buf);
66 :
67 1 : out->data = buf;
68 1 : out->length = in->length;
69 :
70 1 : return NT_STATUS_OK;
71 : }
72 :
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;
79 : };
80 :
81 : /** Prototype of the send callback */
82 : static void echo_udp_call_sendto_done(struct tevent_req *subreq);
83 :
84 : /* Callback to receive UDP packets */
85 1 : static void echo_udp_call_loop(struct tevent_req *subreq)
86 : {
87 : /*
88 : * Our socket structure is the callback data. Get it in a
89 : * type-safe way
90 : */
91 1 : struct echo_udp_socket *sock = tevent_req_callback_data(subreq,
92 : struct echo_udp_socket);
93 0 : struct echo_udp_call *call;
94 0 : uint8_t *buf;
95 0 : ssize_t len;
96 0 : NTSTATUS status;
97 0 : int sys_errno;
98 :
99 1 : call = talloc(sock, struct echo_udp_call);
100 1 : if (call == NULL) {
101 0 : goto done;
102 : }
103 :
104 1 : len = tdgram_recvfrom_recv(subreq, &sys_errno, call, &buf, &call->src);
105 1 : TALLOC_FREE(subreq);
106 1 : if (len == -1) {
107 0 : TALLOC_FREE(call);
108 0 : goto done;
109 : }
110 :
111 1 : call->in.data = buf;
112 1 : call->in.length = len;
113 :
114 1 : DEBUG(10, ("Received echo UDP packet of %lu bytes from %s\n",
115 : (long)len, tsocket_address_string(call->src, call)));
116 :
117 : /* Handle the data coming in and compute the reply */
118 1 : status = echo_process(sock->echo_socket->echo, call,
119 : &call->in, &call->out);
120 1 : if (!NT_STATUS_IS_OK(status)) {
121 0 : TALLOC_FREE(call);
122 0 : DEBUG(0, ("echo_process returned %s\n",
123 : nt_errstr(status)));
124 0 : goto done;
125 : }
126 :
127 : /* I said the task struct would come in handy. */
128 1 : subreq = tdgram_sendto_queue_send(call,
129 1 : sock->echo_socket->echo->task->event_ctx,
130 : sock->dgram,
131 : sock->send_queue,
132 1 : call->out.data,
133 : call->out.length,
134 : call->src);
135 1 : if (subreq == NULL) {
136 0 : TALLOC_FREE(call);
137 0 : goto done;
138 : }
139 :
140 1 : tevent_req_set_callback(subreq, echo_udp_call_sendto_done, call);
141 :
142 1 : done:
143 : /* Now loop for the next incoming UDP packet, the async way */
144 1 : subreq = tdgram_recvfrom_send(sock,
145 1 : sock->echo_socket->echo->task->event_ctx,
146 : sock->dgram);
147 1 : if (subreq == NULL) {
148 0 : task_server_terminate(sock->echo_socket->echo->task,
149 : "no memory for tdgram_recvfrom_send",
150 : true);
151 0 : return;
152 : }
153 1 : tevent_req_set_callback(subreq, echo_udp_call_loop, sock);
154 : }
155 :
156 : /* Callback to send UDP replies */
157 1 : static void echo_udp_call_sendto_done(struct tevent_req *subreq)
158 : {
159 1 : struct echo_udp_call *call = tevent_req_callback_data(subreq,
160 : struct echo_udp_call);
161 0 : int sys_errno;
162 :
163 1 : 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 1 : TALLOC_FREE(call);
171 1 : }
172 :
173 : /* Start listening on a given address */
174 126 : 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 4 : struct echo_socket *echo_socket;
181 4 : struct echo_udp_socket *echo_udp_socket;
182 4 : struct tevent_req *udpsubreq;
183 4 : NTSTATUS status;
184 4 : int ret;
185 :
186 126 : echo_socket = talloc(echo, struct echo_socket);
187 126 : NT_STATUS_HAVE_NO_MEMORY(echo_socket);
188 :
189 126 : 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 126 : ret = tsocket_address_inet_from_strings(echo_socket, "ip",
197 : address, port,
198 : &echo_socket->local_address);
199 126 : if (ret != 0) {
200 0 : status = map_nt_error_from_unix_common(errno);
201 0 : return status;
202 : }
203 :
204 : /* Now set up the udp socket */
205 126 : echo_udp_socket = talloc(echo_socket, struct echo_udp_socket);
206 126 : NT_STATUS_HAVE_NO_MEMORY(echo_udp_socket);
207 :
208 126 : echo_udp_socket->echo_socket = echo_socket;
209 :
210 126 : ret = tdgram_inet_udp_socket(echo_socket->local_address,
211 : NULL,
212 : echo_udp_socket,
213 : &echo_udp_socket->dgram);
214 126 : if (ret != 0) {
215 0 : status = map_nt_error_from_unix_common(errno);
216 0 : DEBUG(0, ("Failed to bind to %s:%u UDP - %s\n",
217 : address, port, nt_errstr(status)));
218 0 : return status;
219 : }
220 :
221 : /*
222 : * We set up a send queue so we can have multiple UDP packets in flight
223 : */
224 126 : echo_udp_socket->send_queue = tevent_queue_create(echo_udp_socket,
225 : "echo_udp_send_queue");
226 126 : 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 130 : udpsubreq = tdgram_recvfrom_send(echo_udp_socket,
233 126 : echo->task->event_ctx,
234 : echo_udp_socket->dgram);
235 126 : NT_STATUS_HAVE_NO_MEMORY(udpsubreq);
236 126 : tevent_req_set_callback(udpsubreq, echo_udp_call_loop, echo_udp_socket);
237 :
238 126 : return NT_STATUS_OK;
239 : }
240 :
241 : /* Set up the listening sockets */
242 63 : 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)
246 : {
247 2 : int num_interfaces;
248 63 : TALLOC_CTX *tmp_ctx = talloc_new(echo);
249 2 : NTSTATUS status;
250 2 : int i;
251 :
252 63 : num_interfaces = iface_list_count(ifaces);
253 :
254 191 : for(i=0; i<num_interfaces; i++) {
255 126 : const char *address = talloc_strdup(tmp_ctx, iface_list_n_ip(ifaces, i));
256 :
257 126 : status = echo_add_socket(echo, model_ops, "echo", address, ECHO_SERVICE_PORT);
258 126 : NT_STATUS_NOT_OK_RETURN(status);
259 : }
260 :
261 63 : TALLOC_FREE(tmp_ctx);
262 63 : return NT_STATUS_OK;
263 : }
264 :
265 :
266 : /* Do the basic task initialization, check if the task should run */
267 :
268 69 : static NTSTATUS echo_task_init(struct task_server *task)
269 : {
270 2 : struct interface *ifaces;
271 2 : struct echo_server *echo;
272 2 : NTSTATUS status;
273 :
274 : /*
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.
277 : */
278 69 : switch(lpcfg_server_role(task->lp_ctx)) {
279 0 : case ROLE_STANDALONE:
280 : /* Yes, we want to run the echo server */
281 61 : break;
282 6 : case ROLE_DOMAIN_MEMBER:
283 6 : task_server_terminate(task, "echo: Not starting echo server " \
284 : "for domain members", false);
285 0 : return NT_STATUS_INVALID_DOMAIN_ROLE;
286 61 : case ROLE_ACTIVE_DIRECTORY_DC:
287 : /* Yes, we want to run the echo server */
288 61 : break;
289 : }
290 :
291 63 : load_interface_list(task, task->lp_ctx, &ifaces);
292 :
293 63 : if (iface_list_count(ifaces) == 0) {
294 0 : task_server_terminate(task,
295 : "echo: No network interfaces configured",
296 : false);
297 0 : return NT_STATUS_UNSUCCESSFUL;
298 : }
299 :
300 63 : task_server_set_title(task, "task[echo]");
301 :
302 63 : echo = talloc_zero(task, struct echo_server);
303 63 : if (echo == NULL) {
304 0 : task_server_terminate(task, "echo: Out of memory", true);
305 0 : return NT_STATUS_NO_MEMORY;
306 : }
307 :
308 63 : echo->task = task;
309 :
310 63 : status = echo_startup_interfaces(echo, task->lp_ctx, ifaces,
311 : task->model_ops);
312 63 : if (!NT_STATUS_IS_OK(status)) {
313 0 : task_server_terminate(task, "echo: Failed to set up interfaces",
314 : true);
315 0 : return status;
316 : }
317 63 : return NT_STATUS_OK;
318 : }
319 :
320 : /*
321 : * Register this server service with the main samba process.
322 : *
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.
325 : */
326 66 : NTSTATUS server_service_echo_init(TALLOC_CTX *ctx)
327 : {
328 3 : static const struct service_details details = {
329 : .inhibit_fork_on_accept = true,
330 : .inhibit_pre_fork = false,
331 : .task_init = echo_task_init,
332 : .post_fork = NULL
333 :
334 : };
335 66 : return register_server_service(ctx, "echo", &details);
336 : }
|