/* * Copyright 2002-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ /*! The socket API directly forwards all requests into the kernel stack via the networking stack driver. */ #include #include #include #include #include #include #include #include #include #include static void convert_from_r5_sockaddr(struct sockaddr *_to, const struct sockaddr *_from) { const r5_sockaddr_in *from = (r5_sockaddr_in *)_from; sockaddr_in *to = (sockaddr_in *)_to; memset(to, 0, sizeof(sockaddr)); to->sin_len = sizeof(sockaddr); if (from == NULL) return; if (from->sin_family == R5_AF_INET) to->sin_family = AF_INET; else to->sin_family = from->sin_family; to->sin_port = from->sin_port; to->sin_addr.s_addr = from->sin_addr; } static void convert_to_r5_sockaddr(struct sockaddr *_to, const struct sockaddr *_from) { const sockaddr_in *from = (sockaddr_in *)_from; r5_sockaddr_in *to = (r5_sockaddr_in *)_to; if (to == NULL) return; memset(to, 0, sizeof(r5_sockaddr_in)); if (from->sin_family == AF_INET) to->sin_family = R5_AF_INET; else to->sin_family = from->sin_family; to->sin_port = from->sin_port; to->sin_addr = from->sin_addr.s_addr; } static void convert_from_r5_socket(int& family, int& type, int& protocol) { switch (family) { case R5_AF_INET: family = AF_INET; break; } switch (type) { case R5_SOCK_DGRAM: type = SOCK_DGRAM; break; case R5_SOCK_STREAM: type = SOCK_STREAM; break; #if 0 case R5_SOCK_RAW: type = SOCK_RAW; break; #endif } switch (protocol) { case R5_IPPROTO_UDP: protocol = IPPROTO_UDP; break; case R5_IPPROTO_TCP: protocol = IPPROTO_TCP; break; case R5_IPPROTO_ICMP: protocol = IPPROTO_ICMP; break; } } static void convert_from_r5_sockopt(int& level, int& option) { if (level == R5_SOL_SOCKET) level = SOL_SOCKET; switch (option) { case R5_SO_DEBUG: option = SO_DEBUG; break; case R5_SO_REUSEADDR: option = SO_REUSEADDR; break; case R5_SO_NONBLOCK: option = SO_NONBLOCK; break; case R5_SO_REUSEPORT: option = SO_REUSEPORT; break; case R5_SO_FIONREAD: // there is no SO_FIONREAD option = -1; break; } } // #pragma mark - extern "C" int socket(int family, int type, int protocol) { if (check_r5_compatibility()) convert_from_r5_socket(family, type, protocol); RETURN_AND_SET_ERRNO(_kern_socket(family, type, protocol)); } extern "C" int bind(int socket, const struct sockaddr *address, socklen_t addressLength) { struct sockaddr haikuAddr; if (check_r5_compatibility()) { convert_from_r5_sockaddr(&haikuAddr, address); address = &haikuAddr; addressLength = sizeof(struct sockaddr_in); } RETURN_AND_SET_ERRNO(_kern_bind(socket, address, addressLength)); } extern "C" int shutdown(int socket, int how) { RETURN_AND_SET_ERRNO(_kern_shutdown_socket(socket, how)); } extern "C" int connect(int socket, const struct sockaddr *address, socklen_t addressLength) { struct sockaddr haikuAddr; if (check_r5_compatibility()) { convert_from_r5_sockaddr(&haikuAddr, address); address = &haikuAddr; addressLength = sizeof(struct sockaddr_in); } RETURN_AND_SET_ERRNO_TEST_CANCEL( _kern_connect(socket, address, addressLength)); } extern "C" int listen(int socket, int backlog) { RETURN_AND_SET_ERRNO(_kern_listen(socket, backlog)); } extern "C" int accept(int socket, struct sockaddr *_address, socklen_t *_addressLength) { bool r5compatible = check_r5_compatibility(); struct sockaddr haikuAddr; sockaddr* address; socklen_t addressLength; if (r5compatible && _address != NULL) { address = &haikuAddr; addressLength = sizeof(haikuAddr); } else { address = _address; addressLength = _addressLength ? *_addressLength : 0; } int acceptSocket = _kern_accept(socket, address, &addressLength); pthread_testcancel(); if (acceptSocket < 0) { errno = acceptSocket; return -1; } if (r5compatible && _address != NULL) { convert_to_r5_sockaddr(_address, &haikuAddr); if (_addressLength != NULL) *_addressLength = sizeof(struct r5_sockaddr_in); } else if (_addressLength != NULL) *_addressLength = addressLength; return acceptSocket; } extern "C" ssize_t recv(int socket, void *data, size_t length, int flags) { RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recv(socket, data, length, flags)); } extern "C" ssize_t recvfrom(int socket, void *data, size_t length, int flags, struct sockaddr *_address, socklen_t *_addressLength) { bool r5compatible = check_r5_compatibility(); struct sockaddr haikuAddr; sockaddr* address; socklen_t addressLength; if (r5compatible && _address != NULL) { address = &haikuAddr; addressLength = sizeof(haikuAddr); } else { address = _address; addressLength = _addressLength ? *_addressLength : 0; } ssize_t bytesReceived = _kern_recvfrom(socket, data, length, flags, address, &addressLength); pthread_testcancel(); if (bytesReceived < 0) { errno = bytesReceived; return -1; } if (r5compatible) { convert_to_r5_sockaddr(_address, &haikuAddr); if (_addressLength != NULL) *_addressLength = sizeof(struct r5_sockaddr_in); } else if (_addressLength != NULL) *_addressLength = addressLength; return bytesReceived; } extern "C" ssize_t recvmsg(int socket, struct msghdr *message, int flags) { RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recvmsg(socket, message, flags)); } extern "C" ssize_t send(int socket, const void *data, size_t length, int flags) { RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_send(socket, data, length, flags)); } extern "C" ssize_t sendto(int socket, const void *data, size_t length, int flags, const struct sockaddr *address, socklen_t addressLength) { struct sockaddr haikuAddr; if (check_r5_compatibility()) { convert_from_r5_sockaddr(&haikuAddr, address); address = &haikuAddr; addressLength = sizeof(struct sockaddr_in); } RETURN_AND_SET_ERRNO_TEST_CANCEL( _kern_sendto(socket, data, length, flags, address, addressLength)); } extern "C" ssize_t sendmsg(int socket, const struct msghdr *message, int flags) { RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_sendmsg(socket, message, flags)); } extern "C" int getsockopt(int socket, int level, int option, void *value, socklen_t *_length) { if (check_r5_compatibility()) { if (option == R5_SO_FIONREAD) { // there is no SO_FIONREAD in our stack; we're using FIONREAD // instead *_length = sizeof(int); return ioctl(socket, FIONREAD, value); } convert_from_r5_sockopt(level, option); } RETURN_AND_SET_ERRNO(_kern_getsockopt(socket, level, option, value, _length)); } extern "C" int setsockopt(int socket, int level, int option, const void *value, socklen_t length) { if (check_r5_compatibility()) convert_from_r5_sockopt(level, option); RETURN_AND_SET_ERRNO(_kern_setsockopt(socket, level, option, value, length)); } extern "C" int getpeername(int socket, struct sockaddr *_address, socklen_t *_addressLength) { bool r5compatible = check_r5_compatibility(); struct sockaddr haikuAddr; sockaddr* address; socklen_t addressLength; if (r5compatible && _address != NULL) { address = &haikuAddr; addressLength = sizeof(haikuAddr); } else { address = _address; addressLength = _addressLength ? *_addressLength : 0; } status_t error = _kern_getpeername(socket, address, &addressLength); if (error != B_OK) { errno = error; return -1; } if (r5compatible) { convert_to_r5_sockaddr(_address, &haikuAddr); if (_addressLength != NULL) *_addressLength = sizeof(struct r5_sockaddr_in); } else if (_addressLength != NULL) *_addressLength = addressLength; return 0; } extern "C" int getsockname(int socket, struct sockaddr *_address, socklen_t *_addressLength) { bool r5compatible = check_r5_compatibility(); struct sockaddr haikuAddr; sockaddr* address; socklen_t addressLength; if (r5compatible && _address != NULL) { address = &haikuAddr; addressLength = sizeof(haikuAddr); } else { address = _address; addressLength = _addressLength ? *_addressLength : 0; } status_t error = _kern_getsockname(socket, address, &addressLength); if (error != B_OK) { errno = error; return -1; } if (r5compatible) { convert_to_r5_sockaddr(_address, &haikuAddr); if (_addressLength != NULL) *_addressLength = sizeof(struct r5_sockaddr_in); } else if (_addressLength != NULL) *_addressLength = addressLength; return 0; } extern "C" int sockatmark(int socket) { RETURN_AND_SET_ERRNO(_kern_sockatmark(socket)); } extern "C" int socketpair(int family, int type, int protocol, int socketVector[2]) { RETURN_AND_SET_ERRNO(_kern_socketpair(family, type, protocol, socketVector)); }