1/* 2 Unix SMB/CIFS implementation. 3 4 unix domain socket functions 5 6 Copyright (C) Stefan Metzmacher 2004 7 Copyright (C) Andrew Tridgell 2004-2005 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 "lib/socket/socket.h" 25#include "system/network.h" 26#include "system/filesys.h" 27 28 29 30/* 31 approximate errno mapping 32*/ 33static NTSTATUS unixdom_error(int ernum) 34{ 35 return map_nt_error_from_unix(ernum); 36} 37 38static NTSTATUS unixdom_init(struct socket_context *sock) 39{ 40 int type; 41 42 switch (sock->type) { 43 case SOCKET_TYPE_STREAM: 44 type = SOCK_STREAM; 45 break; 46 case SOCKET_TYPE_DGRAM: 47 type = SOCK_DGRAM; 48 break; 49 default: 50 return NT_STATUS_INVALID_PARAMETER; 51 } 52 53 sock->fd = socket(PF_UNIX, type, 0); 54 if (sock->fd == -1) { 55 return map_nt_error_from_unix(errno); 56 } 57 sock->private_data = NULL; 58 59 sock->backend_name = "unix"; 60 61 return NT_STATUS_OK; 62} 63 64static void unixdom_close(struct socket_context *sock) 65{ 66 close(sock->fd); 67} 68 69static NTSTATUS unixdom_connect_complete(struct socket_context *sock, uint32_t flags) 70{ 71 int error=0, ret; 72 socklen_t len = sizeof(error); 73 74 /* check for any errors that may have occurred - this is needed 75 for non-blocking connect */ 76 ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len); 77 if (ret == -1) { 78 return map_nt_error_from_unix(errno); 79 } 80 if (error != 0) { 81 return map_nt_error_from_unix(error); 82 } 83 84 if (!(flags & SOCKET_FLAG_BLOCK)) { 85 ret = set_blocking(sock->fd, false); 86 if (ret == -1) { 87 return map_nt_error_from_unix(errno); 88 } 89 } 90 91 sock->state = SOCKET_STATE_CLIENT_CONNECTED; 92 93 return NT_STATUS_OK; 94} 95 96static NTSTATUS unixdom_connect(struct socket_context *sock, 97 const struct socket_address *my_address, 98 const struct socket_address *srv_address, 99 uint32_t flags) 100{ 101 int ret; 102 103 if (srv_address->sockaddr) { 104 ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen); 105 } else { 106 struct sockaddr_un srv_addr; 107 if (strlen(srv_address->addr)+1 > sizeof(srv_addr.sun_path)) { 108 return NT_STATUS_OBJECT_PATH_INVALID; 109 } 110 111 ZERO_STRUCT(srv_addr); 112 srv_addr.sun_family = AF_UNIX; 113 strncpy(srv_addr.sun_path, srv_address->addr, sizeof(srv_addr.sun_path)); 114 115 ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr)); 116 } 117 if (ret == -1) { 118 return unixdom_error(errno); 119 } 120 121 return unixdom_connect_complete(sock, flags); 122} 123 124static NTSTATUS unixdom_listen(struct socket_context *sock, 125 const struct socket_address *my_address, 126 int queue_size, uint32_t flags) 127{ 128 struct sockaddr_un my_addr; 129 int ret; 130 131 /* delete if it already exists */ 132 if (my_address->addr) { 133 unlink(my_address->addr); 134 } 135 136 if (my_address->sockaddr) { 137 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr)); 138 } else if (my_address->addr == NULL) { 139 return NT_STATUS_INVALID_PARAMETER; 140 } else { 141 if (strlen(my_address->addr)+1 > sizeof(my_addr.sun_path)) { 142 return NT_STATUS_OBJECT_PATH_INVALID; 143 } 144 145 146 ZERO_STRUCT(my_addr); 147 my_addr.sun_family = AF_UNIX; 148 strncpy(my_addr.sun_path, my_address->addr, sizeof(my_addr.sun_path)); 149 150 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr)); 151 } 152 if (ret == -1) { 153 return unixdom_error(errno); 154 } 155 156 if (sock->type == SOCKET_TYPE_STREAM) { 157 ret = listen(sock->fd, queue_size); 158 if (ret == -1) { 159 return unixdom_error(errno); 160 } 161 } 162 163 if (!(flags & SOCKET_FLAG_BLOCK)) { 164 ret = set_blocking(sock->fd, false); 165 if (ret == -1) { 166 return unixdom_error(errno); 167 } 168 } 169 170 sock->state = SOCKET_STATE_SERVER_LISTEN; 171 sock->private_data = (void *)talloc_strdup(sock, my_address->addr); 172 173 return NT_STATUS_OK; 174} 175 176static NTSTATUS unixdom_accept(struct socket_context *sock, 177 struct socket_context **new_sock) 178{ 179 struct sockaddr_un cli_addr; 180 socklen_t cli_addr_len = sizeof(cli_addr); 181 int new_fd; 182 183 if (sock->type != SOCKET_TYPE_STREAM) { 184 return NT_STATUS_INVALID_PARAMETER; 185 } 186 187 new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len); 188 if (new_fd == -1) { 189 return unixdom_error(errno); 190 } 191 192 if (!(sock->flags & SOCKET_FLAG_BLOCK)) { 193 int ret = set_blocking(new_fd, false); 194 if (ret == -1) { 195 close(new_fd); 196 return map_nt_error_from_unix(errno); 197 } 198 } 199 200 (*new_sock) = talloc(NULL, struct socket_context); 201 if (!(*new_sock)) { 202 close(new_fd); 203 return NT_STATUS_NO_MEMORY; 204 } 205 206 /* copy the socket_context */ 207 (*new_sock)->type = sock->type; 208 (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED; 209 (*new_sock)->flags = sock->flags; 210 211 (*new_sock)->fd = new_fd; 212 213 (*new_sock)->private_data = NULL; 214 (*new_sock)->ops = sock->ops; 215 (*new_sock)->backend_name = sock->backend_name; 216 217 return NT_STATUS_OK; 218} 219 220static NTSTATUS unixdom_recv(struct socket_context *sock, void *buf, 221 size_t wantlen, size_t *nread) 222{ 223 ssize_t gotlen; 224 225 *nread = 0; 226 227 gotlen = recv(sock->fd, buf, wantlen, 0); 228 if (gotlen == 0) { 229 return NT_STATUS_END_OF_FILE; 230 } else if (gotlen == -1) { 231 return unixdom_error(errno); 232 } 233 234 *nread = gotlen; 235 236 return NT_STATUS_OK; 237} 238 239static NTSTATUS unixdom_send(struct socket_context *sock, 240 const DATA_BLOB *blob, size_t *sendlen) 241{ 242 ssize_t len; 243 244 *sendlen = 0; 245 246 len = send(sock->fd, blob->data, blob->length, 0); 247 if (len == -1) { 248 return unixdom_error(errno); 249 } 250 251 *sendlen = len; 252 253 return NT_STATUS_OK; 254} 255 256 257static NTSTATUS unixdom_sendto(struct socket_context *sock, 258 const DATA_BLOB *blob, size_t *sendlen, 259 const struct socket_address *dest) 260{ 261 ssize_t len; 262 *sendlen = 0; 263 264 if (dest->sockaddr) { 265 len = sendto(sock->fd, blob->data, blob->length, 0, 266 dest->sockaddr, dest->sockaddrlen); 267 } else { 268 struct sockaddr_un srv_addr; 269 270 if (strlen(dest->addr)+1 > sizeof(srv_addr.sun_path)) { 271 return NT_STATUS_OBJECT_PATH_INVALID; 272 } 273 274 ZERO_STRUCT(srv_addr); 275 srv_addr.sun_family = AF_UNIX; 276 strncpy(srv_addr.sun_path, dest->addr, sizeof(srv_addr.sun_path)); 277 278 len = sendto(sock->fd, blob->data, blob->length, 0, 279 (struct sockaddr *)&srv_addr, sizeof(srv_addr)); 280 } 281 if (len == -1) { 282 return map_nt_error_from_unix(errno); 283 } 284 285 *sendlen = len; 286 287 return NT_STATUS_OK; 288} 289 290 291static NTSTATUS unixdom_set_option(struct socket_context *sock, 292 const char *option, const char *val) 293{ 294 return NT_STATUS_OK; 295} 296 297static char *unixdom_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx) 298{ 299 return talloc_strdup(mem_ctx, "LOCAL/unixdom"); 300} 301 302static struct socket_address *unixdom_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx) 303{ 304 struct sockaddr_in *peer_addr; 305 socklen_t len = sizeof(*peer_addr); 306 struct socket_address *peer; 307 int ret; 308 309 peer = talloc(mem_ctx, struct socket_address); 310 if (!peer) { 311 return NULL; 312 } 313 314 peer->family = sock->backend_name; 315 peer_addr = talloc(peer, struct sockaddr_in); 316 if (!peer_addr) { 317 talloc_free(peer); 318 return NULL; 319 } 320 321 peer->sockaddr = (struct sockaddr *)peer_addr; 322 323 ret = getpeername(sock->fd, peer->sockaddr, &len); 324 if (ret == -1) { 325 talloc_free(peer); 326 return NULL; 327 } 328 329 peer->sockaddrlen = len; 330 331 peer->port = 0; 332 peer->addr = talloc_strdup(peer, "LOCAL/unixdom"); 333 if (!peer->addr) { 334 talloc_free(peer); 335 return NULL; 336 } 337 338 return peer; 339} 340 341static struct socket_address *unixdom_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx) 342{ 343 struct sockaddr_in *local_addr; 344 socklen_t len = sizeof(*local_addr); 345 struct socket_address *local; 346 int ret; 347 348 local = talloc(mem_ctx, struct socket_address); 349 if (!local) { 350 return NULL; 351 } 352 353 local->family = sock->backend_name; 354 local_addr = talloc(local, struct sockaddr_in); 355 if (!local_addr) { 356 talloc_free(local); 357 return NULL; 358 } 359 360 local->sockaddr = (struct sockaddr *)local_addr; 361 362 ret = getsockname(sock->fd, local->sockaddr, &len); 363 if (ret == -1) { 364 talloc_free(local); 365 return NULL; 366 } 367 368 local->sockaddrlen = len; 369 370 local->port = 0; 371 local->addr = talloc_strdup(local, "LOCAL/unixdom"); 372 if (!local->addr) { 373 talloc_free(local); 374 return NULL; 375 } 376 377 return local; 378} 379 380static int unixdom_get_fd(struct socket_context *sock) 381{ 382 return sock->fd; 383} 384 385static NTSTATUS unixdom_pending(struct socket_context *sock, size_t *npending) 386{ 387 int value = 0; 388 if (ioctl(sock->fd, FIONREAD, &value) == 0) { 389 *npending = value; 390 return NT_STATUS_OK; 391 } 392 return map_nt_error_from_unix(errno); 393} 394 395static const struct socket_ops unixdom_ops = { 396 .name = "unix", 397 .fn_init = unixdom_init, 398 .fn_connect = unixdom_connect, 399 .fn_connect_complete = unixdom_connect_complete, 400 .fn_listen = unixdom_listen, 401 .fn_accept = unixdom_accept, 402 .fn_recv = unixdom_recv, 403 .fn_send = unixdom_send, 404 .fn_sendto = unixdom_sendto, 405 .fn_close = unixdom_close, 406 .fn_pending = unixdom_pending, 407 408 .fn_set_option = unixdom_set_option, 409 410 .fn_get_peer_name = unixdom_get_peer_name, 411 .fn_get_peer_addr = unixdom_get_peer_addr, 412 .fn_get_my_addr = unixdom_get_my_addr, 413 414 .fn_get_fd = unixdom_get_fd 415}; 416 417_PUBLIC_ const struct socket_ops *socket_unixdom_ops(enum socket_type type) 418{ 419 return &unixdom_ops; 420} 421