LCOV - code coverage report
Current view: top level - source4/echo_server - echo_server.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 90 119 75.6 %
Date: 2024-04-21 15:09:00 Functions: 7 7 100.0 %

          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             : }

Generated by: LCOV version 1.14