| 1 | /*
|
|---|
| 2 | Unix SMB/CIFS implementation.
|
|---|
| 3 |
|
|---|
| 4 | helper functions for stream based servers
|
|---|
| 5 |
|
|---|
| 6 | Copyright (C) Andrew Tridgell 2003-2005
|
|---|
| 7 | Copyright (C) Stefan (metze) Metzmacher 2004
|
|---|
| 8 |
|
|---|
| 9 | This program is free software; you can redistribute it and/or modify
|
|---|
| 10 | it under the terms of the GNU General Public License as published by
|
|---|
| 11 | the Free Software Foundation; either version 3 of the License, or
|
|---|
| 12 | (at your option) any later version.
|
|---|
| 13 |
|
|---|
| 14 | This program is distributed in the hope that it will be useful,
|
|---|
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 17 | GNU General Public License for more details.
|
|---|
| 18 |
|
|---|
| 19 | You should have received a copy of the GNU General Public License
|
|---|
| 20 | along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|---|
| 21 | */
|
|---|
| 22 |
|
|---|
| 23 | #include "includes.h"
|
|---|
| 24 | #include <tevent.h>
|
|---|
| 25 | #include "process_model.h"
|
|---|
| 26 | #include "lib/messaging/irpc.h"
|
|---|
| 27 | #include "cluster/cluster.h"
|
|---|
| 28 | #include "param/param.h"
|
|---|
| 29 |
|
|---|
| 30 | /* the range of ports to try for dcerpc over tcp endpoints */
|
|---|
| 31 | #define SERVER_TCP_LOW_PORT 1024
|
|---|
| 32 | #define SERVER_TCP_HIGH_PORT 1300
|
|---|
| 33 |
|
|---|
| 34 | /* size of listen() backlog in smbd */
|
|---|
| 35 | #define SERVER_LISTEN_BACKLOG 10
|
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 | /*
|
|---|
| 39 | private structure for a single listening stream socket
|
|---|
| 40 | */
|
|---|
| 41 | struct stream_socket {
|
|---|
| 42 | const struct stream_server_ops *ops;
|
|---|
| 43 | struct loadparm_context *lp_ctx;
|
|---|
| 44 | struct tevent_context *event_ctx;
|
|---|
| 45 | const struct model_ops *model_ops;
|
|---|
| 46 | struct socket_context *sock;
|
|---|
| 47 | void *private_data;
|
|---|
| 48 | };
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 | /*
|
|---|
| 52 | close the socket and shutdown a stream_connection
|
|---|
| 53 | */
|
|---|
| 54 | void stream_terminate_connection(struct stream_connection *srv_conn, const char *reason)
|
|---|
| 55 | {
|
|---|
| 56 | struct tevent_context *event_ctx = srv_conn->event.ctx;
|
|---|
| 57 | const struct model_ops *model_ops = srv_conn->model_ops;
|
|---|
| 58 |
|
|---|
| 59 | if (!reason) reason = "unknown reason";
|
|---|
| 60 |
|
|---|
| 61 | DEBUG(3,("Terminating connection - '%s'\n", reason));
|
|---|
| 62 |
|
|---|
| 63 | srv_conn->terminate = reason;
|
|---|
| 64 |
|
|---|
| 65 | if (srv_conn->processing) {
|
|---|
| 66 | /*
|
|---|
| 67 | * if we're currently inside the stream_io_handler(),
|
|---|
| 68 | * defer the termination to the end of stream_io_hendler()
|
|---|
| 69 | *
|
|---|
| 70 | * and we don't want to read or write to the connection...
|
|---|
| 71 | */
|
|---|
| 72 | tevent_fd_set_flags(srv_conn->event.fde, 0);
|
|---|
| 73 | return;
|
|---|
| 74 | }
|
|---|
| 75 |
|
|---|
| 76 | talloc_free(srv_conn->event.fde);
|
|---|
| 77 | srv_conn->event.fde = NULL;
|
|---|
| 78 | model_ops->terminate(event_ctx, srv_conn->lp_ctx, reason);
|
|---|
| 79 | talloc_free(srv_conn);
|
|---|
| 80 | }
|
|---|
| 81 |
|
|---|
| 82 | /**
|
|---|
| 83 | the select loop has indicated that a stream is ready for IO
|
|---|
| 84 | */
|
|---|
| 85 | static void stream_io_handler(struct stream_connection *conn, uint16_t flags)
|
|---|
| 86 | {
|
|---|
| 87 | conn->processing++;
|
|---|
| 88 | if (flags & TEVENT_FD_WRITE) {
|
|---|
| 89 | conn->ops->send_handler(conn, flags);
|
|---|
| 90 | } else if (flags & TEVENT_FD_READ) {
|
|---|
| 91 | conn->ops->recv_handler(conn, flags);
|
|---|
| 92 | }
|
|---|
| 93 | conn->processing--;
|
|---|
| 94 |
|
|---|
| 95 | if (conn->terminate) {
|
|---|
| 96 | stream_terminate_connection(conn, conn->terminate);
|
|---|
| 97 | }
|
|---|
| 98 | }
|
|---|
| 99 |
|
|---|
| 100 | static void stream_io_handler_fde(struct tevent_context *ev, struct tevent_fd *fde,
|
|---|
| 101 | uint16_t flags, void *private_data)
|
|---|
| 102 | {
|
|---|
| 103 | struct stream_connection *conn = talloc_get_type(private_data,
|
|---|
| 104 | struct stream_connection);
|
|---|
| 105 | stream_io_handler(conn, flags);
|
|---|
| 106 | }
|
|---|
| 107 |
|
|---|
| 108 | void stream_io_handler_callback(void *private_data, uint16_t flags)
|
|---|
| 109 | {
|
|---|
| 110 | struct stream_connection *conn = talloc_get_type(private_data,
|
|---|
| 111 | struct stream_connection);
|
|---|
| 112 | stream_io_handler(conn, flags);
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | /*
|
|---|
| 116 | this creates a stream_connection from an already existing connection,
|
|---|
| 117 | used for protocols, where a client connection needs to switched into
|
|---|
| 118 | a server connection
|
|---|
| 119 | */
|
|---|
| 120 | NTSTATUS stream_new_connection_merge(struct tevent_context *ev,
|
|---|
| 121 | struct loadparm_context *lp_ctx,
|
|---|
| 122 | const struct model_ops *model_ops,
|
|---|
| 123 | struct socket_context *sock,
|
|---|
| 124 | const struct stream_server_ops *stream_ops,
|
|---|
| 125 | struct messaging_context *msg_ctx,
|
|---|
| 126 | void *private_data,
|
|---|
| 127 | struct stream_connection **_srv_conn)
|
|---|
| 128 | {
|
|---|
| 129 | struct stream_connection *srv_conn;
|
|---|
| 130 |
|
|---|
| 131 | srv_conn = talloc_zero(ev, struct stream_connection);
|
|---|
| 132 | NT_STATUS_HAVE_NO_MEMORY(srv_conn);
|
|---|
| 133 |
|
|---|
| 134 | talloc_steal(srv_conn, sock);
|
|---|
| 135 |
|
|---|
| 136 | srv_conn->private_data = private_data;
|
|---|
| 137 | srv_conn->model_ops = model_ops;
|
|---|
| 138 | srv_conn->socket = sock;
|
|---|
| 139 | srv_conn->server_id = cluster_id(0, 0);
|
|---|
| 140 | srv_conn->ops = stream_ops;
|
|---|
| 141 | srv_conn->msg_ctx = msg_ctx;
|
|---|
| 142 | srv_conn->event.ctx = ev;
|
|---|
| 143 | srv_conn->lp_ctx = lp_ctx;
|
|---|
| 144 | srv_conn->event.fde = tevent_add_fd(ev, srv_conn, socket_get_fd(sock),
|
|---|
| 145 | TEVENT_FD_READ,
|
|---|
| 146 | stream_io_handler_fde, srv_conn);
|
|---|
| 147 | if (!srv_conn->event.fde) {
|
|---|
| 148 | talloc_free(srv_conn);
|
|---|
| 149 | return NT_STATUS_NO_MEMORY;
|
|---|
| 150 | }
|
|---|
| 151 |
|
|---|
| 152 | *_srv_conn = srv_conn;
|
|---|
| 153 | return NT_STATUS_OK;
|
|---|
| 154 | }
|
|---|
| 155 |
|
|---|
| 156 | /*
|
|---|
| 157 | called when a new socket connection has been established. This is called in the process
|
|---|
| 158 | context of the new process (if appropriate)
|
|---|
| 159 | */
|
|---|
| 160 | static void stream_new_connection(struct tevent_context *ev,
|
|---|
| 161 | struct loadparm_context *lp_ctx,
|
|---|
| 162 | struct socket_context *sock,
|
|---|
| 163 | struct server_id server_id, void *private_data)
|
|---|
| 164 | {
|
|---|
| 165 | struct stream_socket *stream_socket = talloc_get_type(private_data, struct stream_socket);
|
|---|
| 166 | struct stream_connection *srv_conn;
|
|---|
| 167 | struct socket_address *c, *s;
|
|---|
| 168 |
|
|---|
| 169 | srv_conn = talloc_zero(ev, struct stream_connection);
|
|---|
| 170 | if (!srv_conn) {
|
|---|
| 171 | DEBUG(0,("talloc(mem_ctx, struct stream_connection) failed\n"));
|
|---|
| 172 | return;
|
|---|
| 173 | }
|
|---|
| 174 |
|
|---|
| 175 | talloc_steal(srv_conn, sock);
|
|---|
| 176 |
|
|---|
| 177 | srv_conn->private_data = stream_socket->private_data;
|
|---|
| 178 | srv_conn->model_ops = stream_socket->model_ops;
|
|---|
| 179 | srv_conn->socket = sock;
|
|---|
| 180 | srv_conn->server_id = server_id;
|
|---|
| 181 | srv_conn->ops = stream_socket->ops;
|
|---|
| 182 | srv_conn->event.ctx = ev;
|
|---|
| 183 | srv_conn->lp_ctx = lp_ctx;
|
|---|
| 184 |
|
|---|
| 185 | if (!socket_check_access(sock, "smbd", lp_hostsallow(NULL, lp_default_service(lp_ctx)), lp_hostsdeny(NULL, lp_default_service(lp_ctx)))) {
|
|---|
| 186 | stream_terminate_connection(srv_conn, "denied by access rules");
|
|---|
| 187 | return;
|
|---|
| 188 | }
|
|---|
| 189 |
|
|---|
| 190 | srv_conn->event.fde = tevent_add_fd(ev, srv_conn, socket_get_fd(sock),
|
|---|
| 191 | 0, stream_io_handler_fde, srv_conn);
|
|---|
| 192 | if (!srv_conn->event.fde) {
|
|---|
| 193 | stream_terminate_connection(srv_conn, "tevent_add_fd() failed");
|
|---|
| 194 | return;
|
|---|
| 195 | }
|
|---|
| 196 |
|
|---|
| 197 | /* setup to receive internal messages on this connection */
|
|---|
| 198 | srv_conn->msg_ctx = messaging_init(srv_conn,
|
|---|
| 199 | lp_messaging_path(srv_conn, lp_ctx),
|
|---|
| 200 | srv_conn->server_id,
|
|---|
| 201 | lp_iconv_convenience(lp_ctx),
|
|---|
| 202 | ev);
|
|---|
| 203 | if (!srv_conn->msg_ctx) {
|
|---|
| 204 | stream_terminate_connection(srv_conn, "messaging_init() failed");
|
|---|
| 205 | return;
|
|---|
| 206 | }
|
|---|
| 207 |
|
|---|
| 208 | c = socket_get_peer_addr(sock, ev);
|
|---|
| 209 | s = socket_get_my_addr(sock, ev);
|
|---|
| 210 | if (s && c) {
|
|---|
| 211 | const char *title;
|
|---|
| 212 | title = talloc_asprintf(s, "conn[%s] c[%s:%u] s[%s:%u] server_id[%s]",
|
|---|
| 213 | stream_socket->ops->name,
|
|---|
| 214 | c->addr, c->port, s->addr, s->port,
|
|---|
| 215 | cluster_id_string(s, server_id));
|
|---|
| 216 | if (title) {
|
|---|
| 217 | stream_connection_set_title(srv_conn, title);
|
|---|
| 218 | }
|
|---|
| 219 | }
|
|---|
| 220 | talloc_free(c);
|
|---|
| 221 | talloc_free(s);
|
|---|
| 222 |
|
|---|
| 223 | /* we're now ready to start receiving events on this stream */
|
|---|
| 224 | TEVENT_FD_READABLE(srv_conn->event.fde);
|
|---|
| 225 |
|
|---|
| 226 | /* call the server specific accept code */
|
|---|
| 227 | stream_socket->ops->accept_connection(srv_conn);
|
|---|
| 228 | }
|
|---|
| 229 |
|
|---|
| 230 |
|
|---|
| 231 | /*
|
|---|
| 232 | called when someone opens a connection to one of our listening ports
|
|---|
| 233 | */
|
|---|
| 234 | static void stream_accept_handler(struct tevent_context *ev, struct tevent_fd *fde,
|
|---|
| 235 | uint16_t flags, void *private_data)
|
|---|
| 236 | {
|
|---|
| 237 | struct stream_socket *stream_socket = talloc_get_type(private_data, struct stream_socket);
|
|---|
| 238 |
|
|---|
| 239 | /* ask the process model to create us a process for this new
|
|---|
| 240 | connection. When done, it calls stream_new_connection()
|
|---|
| 241 | with the newly created socket */
|
|---|
| 242 | stream_socket->model_ops->accept_connection(ev, stream_socket->lp_ctx,
|
|---|
| 243 | stream_socket->sock,
|
|---|
| 244 | stream_new_connection, stream_socket);
|
|---|
| 245 | }
|
|---|
| 246 |
|
|---|
| 247 | /*
|
|---|
| 248 | setup a listen stream socket
|
|---|
| 249 | if you pass *port == 0, then a port > 1024 is used
|
|---|
| 250 |
|
|---|
| 251 | FIXME: This function is TCP/IP specific - uses an int rather than
|
|---|
| 252 | a string for the port. Should leave allocating a port nr
|
|---|
| 253 | to the socket implementation - JRV20070903
|
|---|
| 254 | */
|
|---|
| 255 | NTSTATUS stream_setup_socket(struct tevent_context *event_context,
|
|---|
| 256 | struct loadparm_context *lp_ctx,
|
|---|
| 257 | const struct model_ops *model_ops,
|
|---|
| 258 | const struct stream_server_ops *stream_ops,
|
|---|
| 259 | const char *family,
|
|---|
| 260 | const char *sock_addr,
|
|---|
| 261 | uint16_t *port,
|
|---|
| 262 | const char *socket_options,
|
|---|
| 263 | void *private_data)
|
|---|
| 264 | {
|
|---|
| 265 | NTSTATUS status;
|
|---|
| 266 | struct stream_socket *stream_socket;
|
|---|
| 267 | struct socket_address *socket_address;
|
|---|
| 268 | struct tevent_fd *fde;
|
|---|
| 269 | int i;
|
|---|
| 270 |
|
|---|
| 271 | stream_socket = talloc_zero(event_context, struct stream_socket);
|
|---|
| 272 | NT_STATUS_HAVE_NO_MEMORY(stream_socket);
|
|---|
| 273 |
|
|---|
| 274 | status = socket_create(family, SOCKET_TYPE_STREAM, &stream_socket->sock, 0);
|
|---|
| 275 | NT_STATUS_NOT_OK_RETURN(status);
|
|---|
| 276 |
|
|---|
| 277 | talloc_steal(stream_socket, stream_socket->sock);
|
|---|
| 278 |
|
|---|
| 279 | stream_socket->lp_ctx = talloc_reference(stream_socket, lp_ctx);
|
|---|
| 280 |
|
|---|
| 281 | /* ready to listen */
|
|---|
| 282 | status = socket_set_option(stream_socket->sock, "SO_KEEPALIVE", NULL);
|
|---|
| 283 | NT_STATUS_NOT_OK_RETURN(status);
|
|---|
| 284 |
|
|---|
| 285 | if (socket_options != NULL) {
|
|---|
| 286 | status = socket_set_option(stream_socket->sock, socket_options, NULL);
|
|---|
| 287 | NT_STATUS_NOT_OK_RETURN(status);
|
|---|
| 288 | }
|
|---|
| 289 |
|
|---|
| 290 | /* TODO: set socket ACL's (host allow etc) here when they're
|
|---|
| 291 | * implemented */
|
|---|
| 292 |
|
|---|
| 293 | /* Some sockets don't have a port, or are just described from
|
|---|
| 294 | * the string. We are indicating this by having port == NULL */
|
|---|
| 295 | if (!port) {
|
|---|
| 296 | socket_address = socket_address_from_strings(stream_socket,
|
|---|
| 297 | stream_socket->sock->backend_name,
|
|---|
| 298 | sock_addr, 0);
|
|---|
| 299 | NT_STATUS_HAVE_NO_MEMORY(socket_address);
|
|---|
| 300 | status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0);
|
|---|
| 301 | talloc_free(socket_address);
|
|---|
| 302 |
|
|---|
| 303 | } else if (*port == 0) {
|
|---|
| 304 | for (i=SERVER_TCP_LOW_PORT;i<= SERVER_TCP_HIGH_PORT;i++) {
|
|---|
| 305 | socket_address = socket_address_from_strings(stream_socket,
|
|---|
| 306 | stream_socket->sock->backend_name,
|
|---|
| 307 | sock_addr, i);
|
|---|
| 308 | NT_STATUS_HAVE_NO_MEMORY(socket_address);
|
|---|
| 309 | status = socket_listen(stream_socket->sock, socket_address,
|
|---|
| 310 | SERVER_LISTEN_BACKLOG, 0);
|
|---|
| 311 | talloc_free(socket_address);
|
|---|
| 312 | if (NT_STATUS_IS_OK(status)) {
|
|---|
| 313 | *port = i;
|
|---|
| 314 | break;
|
|---|
| 315 | }
|
|---|
| 316 | }
|
|---|
| 317 | } else {
|
|---|
| 318 | socket_address = socket_address_from_strings(stream_socket,
|
|---|
| 319 | stream_socket->sock->backend_name,
|
|---|
| 320 | sock_addr, *port);
|
|---|
| 321 | NT_STATUS_HAVE_NO_MEMORY(socket_address);
|
|---|
| 322 | status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0);
|
|---|
| 323 | talloc_free(socket_address);
|
|---|
| 324 | }
|
|---|
| 325 |
|
|---|
| 326 | if (!NT_STATUS_IS_OK(status)) {
|
|---|
| 327 | DEBUG(0,("Failed to listen on %s:%u - %s\n",
|
|---|
| 328 | sock_addr, port ? (unsigned int)(*port) : 0,
|
|---|
| 329 | nt_errstr(status)));
|
|---|
| 330 | talloc_free(stream_socket);
|
|---|
| 331 | return status;
|
|---|
| 332 | }
|
|---|
| 333 |
|
|---|
| 334 | /* Add the FD from the newly created socket into the event
|
|---|
| 335 | * subsystem. it will call the accept handler whenever we get
|
|---|
| 336 | * new connections */
|
|---|
| 337 |
|
|---|
| 338 | fde = tevent_add_fd(event_context, stream_socket->sock,
|
|---|
| 339 | socket_get_fd(stream_socket->sock),
|
|---|
| 340 | TEVENT_FD_READ,
|
|---|
| 341 | stream_accept_handler, stream_socket);
|
|---|
| 342 | if (!fde) {
|
|---|
| 343 | DEBUG(0,("Failed to setup fd event\n"));
|
|---|
| 344 | talloc_free(stream_socket);
|
|---|
| 345 | return NT_STATUS_NO_MEMORY;
|
|---|
| 346 | }
|
|---|
| 347 |
|
|---|
| 348 | /* we let events system to the close on the socket. This avoids
|
|---|
| 349 | * nasty interactions with waiting for talloc to close the socket. */
|
|---|
| 350 | tevent_fd_set_close_fn(fde, socket_tevent_fd_close_fn);
|
|---|
| 351 | socket_set_flags(stream_socket->sock, SOCKET_FLAG_NOCLOSE);
|
|---|
| 352 |
|
|---|
| 353 | stream_socket->private_data = talloc_reference(stream_socket, private_data);
|
|---|
| 354 | stream_socket->ops = stream_ops;
|
|---|
| 355 | stream_socket->event_ctx = event_context;
|
|---|
| 356 | stream_socket->model_ops = model_ops;
|
|---|
| 357 |
|
|---|
| 358 | return NT_STATUS_OK;
|
|---|
| 359 | }
|
|---|
| 360 |
|
|---|
| 361 | /*
|
|---|
| 362 | setup a connection title
|
|---|
| 363 | */
|
|---|
| 364 | void stream_connection_set_title(struct stream_connection *conn, const char *title)
|
|---|
| 365 | {
|
|---|
| 366 | conn->model_ops->set_title(conn->event.ctx, title);
|
|---|
| 367 | }
|
|---|