* Completed the previous commit and merger of the team/network/new_stack branch.
* Removed ppp_up and pppcontrol from the image for now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18457 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Axel Dörfler, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "stack_private.h"
|
||||
|
||||
#include <net_protocol.h>
|
||||
#include <net_stack.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
status_t
|
||||
create_socket(int family, int type, int protocol, net_socket **_socket)
|
||||
{
|
||||
struct net_socket *socket = new (std::nothrow) net_socket;
|
||||
if (socket == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memset(socket, 0, sizeof(net_socket));
|
||||
socket->family = family;
|
||||
socket->type = type;
|
||||
socket->protocol = protocol;
|
||||
|
||||
// set defaults (may be overridden by the protocols)
|
||||
socket->send.buffer_size = 65536;
|
||||
socket->send.low_water_mark = 1;
|
||||
socket->send.timeout = B_INFINITE_TIMEOUT;
|
||||
socket->receive.buffer_size = 65536;
|
||||
socket->receive.low_water_mark = 1;
|
||||
socket->receive.timeout = B_INFINITE_TIMEOUT;
|
||||
|
||||
status_t status = get_domain_protocols(socket);
|
||||
if (status < B_OK) {
|
||||
delete socket;
|
||||
return status;
|
||||
}
|
||||
|
||||
status = socket->first_info->open(socket->first_protocol);
|
||||
if (status < B_OK) {
|
||||
put_domain_protocols(socket);
|
||||
delete socket;
|
||||
return status;
|
||||
}
|
||||
|
||||
*_socket = socket;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
socket_close(net_socket *socket)
|
||||
{
|
||||
return socket->first_info->close(socket->first_protocol);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
socket_free(net_socket *socket)
|
||||
{
|
||||
status_t status = socket->first_info->free(socket->first_protocol);
|
||||
|
||||
put_domain_protocols(socket);
|
||||
delete socket;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
socket_readv(net_socket *socket, const iovec *vecs, size_t vecCount, size_t *_length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
socket_writev(net_socket *socket, const iovec *vecs, size_t vecCount, size_t *_length)
|
||||
{
|
||||
if (socket->peer.ss_len == 0)
|
||||
return ECONNRESET;
|
||||
|
||||
if (socket->address.ss_len == 0) {
|
||||
// TODO: bind?!
|
||||
return ENETUNREACH;
|
||||
}
|
||||
|
||||
// TODO: useful, maybe even computed header space!
|
||||
net_buffer *buffer = gNetBufferModule.create(256);
|
||||
if (buffer == NULL)
|
||||
return ENOBUFS;
|
||||
|
||||
// copy data into buffer
|
||||
|
||||
for (uint32 i = 0; i < vecCount; i++) {
|
||||
if (gNetBufferModule.append(buffer, vecs[i].iov_base,
|
||||
vecs[i].iov_len) < B_OK) {
|
||||
gNetBufferModule.free(buffer);
|
||||
return ENOBUFS;
|
||||
}
|
||||
}
|
||||
|
||||
//buffer->source = (sockaddr *)&socket->address;
|
||||
//buffer->destination = (sockaddr *)&socket->peer;
|
||||
memcpy(&buffer->source, &socket->address, socket->address.ss_len);
|
||||
memcpy(&buffer->destination, &socket->peer, socket->peer.ss_len);
|
||||
|
||||
ssize_t bytesWritten = socket->first_info->send_data(socket->first_protocol,
|
||||
buffer);
|
||||
if (bytesWritten < B_OK) {
|
||||
*_length = 0;
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
*_length = bytesWritten;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
socket_control(net_socket *socket, int32 op, void *data, size_t length)
|
||||
{
|
||||
return socket->first_info->control(socket->first_protocol,
|
||||
LEVEL_DRIVER_IOCTL, op, data, &length);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
socket_read_avail(net_socket *socket)
|
||||
{
|
||||
return socket->first_info->read_avail(socket->first_protocol);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
socket_send_avail(net_socket *socket)
|
||||
{
|
||||
return socket->first_info->send_avail(socket->first_protocol);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
socket_send_data(net_socket *socket, net_buffer *buffer)
|
||||
{
|
||||
return socket->first_info->send_data(socket->first_protocol,
|
||||
buffer);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
socket_receive_data(net_socket *socket, size_t length, uint32 flags,
|
||||
net_buffer **_buffer)
|
||||
{
|
||||
return socket->first_info->read_data(socket->first_protocol,
|
||||
length, flags, _buffer);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - standard socket API
|
||||
|
||||
|
||||
int
|
||||
socket_accept(net_socket *socket, struct sockaddr *address, socklen_t *_addressLength,
|
||||
net_socket **_acceptedSocket)
|
||||
{
|
||||
net_socket *accepted;
|
||||
status_t status = socket->first_info->accept(socket->first_protocol,
|
||||
&accepted);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
if (address && *_addressLength > 0) {
|
||||
memcpy(address, &accepted->peer, min_c(*_addressLength, accepted->peer.ss_len));
|
||||
*_addressLength = accepted->peer.ss_len;
|
||||
}
|
||||
|
||||
*_acceptedSocket = accepted;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
socket_bind(net_socket *socket, const struct sockaddr *address, socklen_t addressLength)
|
||||
{
|
||||
sockaddr empty;
|
||||
if (address == NULL) {
|
||||
// special - try to bind to an empty address, like INADDR_ANY
|
||||
memset(&empty, 0, sizeof(sockaddr));
|
||||
empty.sa_len = sizeof(sockaddr);
|
||||
empty.sa_family = socket->family;
|
||||
|
||||
address = ∅
|
||||
addressLength = sizeof(sockaddr);
|
||||
}
|
||||
|
||||
if (socket->address.ss_len != 0) {
|
||||
status_t status = socket->first_info->unbind(socket->first_protocol,
|
||||
(sockaddr *)&socket->address);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
memcpy(&socket->address, address, sizeof(sockaddr));
|
||||
|
||||
status_t status = socket->first_info->bind(socket->first_protocol,
|
||||
(sockaddr *)address);
|
||||
if (status < B_OK) {
|
||||
// clear address again, as binding failed
|
||||
socket->address.ss_len = 0;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
socket_connect(net_socket *socket, const struct sockaddr *address, socklen_t addressLength)
|
||||
{
|
||||
if (address == NULL || addressLength == 0)
|
||||
return ENETUNREACH;
|
||||
|
||||
if (socket->address.ss_len == 0) {
|
||||
// try to bind first
|
||||
status_t status = socket_bind(socket, NULL, 0);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
return socket->first_info->connect(socket->first_protocol, address);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
socket_getpeername(net_socket *socket, struct sockaddr *address, socklen_t *_addressLength)
|
||||
{
|
||||
if (socket->peer.ss_len == 0)
|
||||
return ENOTCONN;
|
||||
|
||||
memcpy(address, &socket->peer, min_c(*_addressLength, socket->peer.ss_len));
|
||||
*_addressLength = socket->peer.ss_len;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
socket_getsockname(net_socket *socket, struct sockaddr *address, socklen_t *_addressLength)
|
||||
{
|
||||
if (socket->address.ss_len == 0)
|
||||
return ENOTCONN;
|
||||
|
||||
memcpy(address, &socket->address, min_c(*_addressLength, socket->address.ss_len));
|
||||
*_addressLength = socket->address.ss_len;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
socket_getsockopt(net_socket *socket, int level, int option, void *value,
|
||||
int *_length)
|
||||
{
|
||||
if (level != SOL_SOCKET) {
|
||||
return socket->first_info->control(socket->first_protocol,
|
||||
level | LEVEL_GET_OPTION, option, value, (size_t *)_length);
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case SO_SNDBUF:
|
||||
{
|
||||
uint32 *size = (uint32 *)value;
|
||||
*size = socket->send.buffer_size;
|
||||
*_length = sizeof(uint32);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
case SO_RCVBUF:
|
||||
{
|
||||
uint32 *size = (uint32 *)value;
|
||||
*size = socket->receive.buffer_size;
|
||||
*_length = sizeof(uint32);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ENOPROTOOPT;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
socket_listen(net_socket *socket, int backlog)
|
||||
{
|
||||
return socket->first_info->listen(socket->first_protocol, backlog);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
socket_recv(net_socket *socket, void *data, size_t length, int flags)
|
||||
{
|
||||
net_buffer *buffer;
|
||||
status_t status = socket->first_info->read_data(
|
||||
socket->first_protocol, length, flags, &buffer);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
ssize_t bytesReceived = buffer->size;
|
||||
gNetBufferModule.read(buffer, 0, data, bytesReceived);
|
||||
gNetBufferModule.free(buffer);
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
socket_recvfrom(net_socket *socket, void *data, size_t length, int flags,
|
||||
struct sockaddr *address, socklen_t *_addressLength)
|
||||
{
|
||||
net_buffer *buffer;
|
||||
status_t status = socket->first_info->read_data(
|
||||
socket->first_protocol, length, flags, &buffer);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
ssize_t bytesReceived = buffer->size;
|
||||
gNetBufferModule.read(buffer, 0, data, bytesReceived);
|
||||
|
||||
// copy source address
|
||||
if (address != NULL && *_addressLength > 0) {
|
||||
*_addressLength = min_c(buffer->source.ss_len, *_addressLength);
|
||||
memcpy(address, &buffer->source, *_addressLength);
|
||||
}
|
||||
|
||||
gNetBufferModule.free(buffer);
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
socket_send(net_socket *socket, const void *data, size_t length, int flags)
|
||||
{
|
||||
if (socket->peer.ss_len == 0)
|
||||
return EDESTADDRREQ;
|
||||
|
||||
if (socket->address.ss_len == 0) {
|
||||
// try to bind first
|
||||
status_t status = socket_bind(socket, NULL, 0);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
// TODO: useful, maybe even computed header space!
|
||||
net_buffer *buffer = gNetBufferModule.create(256);
|
||||
if (buffer == NULL)
|
||||
return ENOBUFS;
|
||||
|
||||
// copy data into buffer
|
||||
if (gNetBufferModule.append(buffer, data, length) < B_OK) {
|
||||
gNetBufferModule.free(buffer);
|
||||
return ENOBUFS;
|
||||
}
|
||||
|
||||
buffer->flags = flags;
|
||||
//buffer->source = (sockaddr *)&socket->address;
|
||||
//buffer->destination = (sockaddr *)&socket->peer;
|
||||
memcpy(&buffer->source, &socket->address, socket->address.ss_len);
|
||||
memcpy(&buffer->destination, &socket->peer, socket->peer.ss_len);
|
||||
|
||||
status_t status = socket->first_info->send_data(socket->first_protocol, buffer);
|
||||
if (status < B_OK) {
|
||||
gNetBufferModule.free(buffer);
|
||||
return status;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
socket_sendto(net_socket *socket, const void *data, size_t length, int flags,
|
||||
const struct sockaddr *address, socklen_t addressLength)
|
||||
{
|
||||
if ((address == NULL || addressLength == 0) && socket->peer.ss_len != 0) {
|
||||
// socket is connected, we use that address:
|
||||
address = (struct sockaddr *)&socket->peer;
|
||||
addressLength = socket->peer.ss_len;
|
||||
}
|
||||
if (address == NULL || addressLength == 0) {
|
||||
// don't know where to send to:
|
||||
return EDESTADDRREQ;
|
||||
}
|
||||
if (socket->peer.ss_len != 0) {
|
||||
// an address has been given but socket is connected already:
|
||||
return EISCONN;
|
||||
}
|
||||
|
||||
if (socket->address.ss_len == 0) {
|
||||
// try to bind first
|
||||
status_t status = socket_bind(socket, NULL, 0);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
// TODO: useful, maybe even computed header space!
|
||||
net_buffer *buffer = gNetBufferModule.create(256);
|
||||
if (buffer == NULL)
|
||||
return ENOBUFS;
|
||||
|
||||
// copy data into buffer
|
||||
if (gNetBufferModule.append(buffer, data, length) < B_OK) {
|
||||
gNetBufferModule.free(buffer);
|
||||
return ENOBUFS;
|
||||
}
|
||||
|
||||
buffer->flags = flags;
|
||||
memcpy(&buffer->source, &socket->address, socket->address.ss_len);
|
||||
memcpy(&buffer->destination, address, addressLength);
|
||||
|
||||
status_t status = socket->first_info->send_data(socket->first_protocol, buffer);
|
||||
if (status < B_OK) {
|
||||
gNetBufferModule.free(buffer);
|
||||
return status;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
socket_setsockopt(net_socket *socket, int level, int option, const void *value,
|
||||
int length)
|
||||
{
|
||||
if (level != SOL_SOCKET) {
|
||||
return socket->first_info->control(socket->first_protocol,
|
||||
level | LEVEL_SET_OPTION, option, (void *)value, (size_t *)&length);
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
// TODO: implement other options!
|
||||
case SO_LINGER:
|
||||
{
|
||||
if (length < (int)sizeof(struct linger))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
struct linger *linger = (struct linger *)value;
|
||||
if (linger->l_onoff) {
|
||||
socket->options |= SO_LINGER;
|
||||
socket->linger = linger->l_linger;
|
||||
} else {
|
||||
socket->options &= ~SO_LINGER;
|
||||
socket->linger = 0;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
case SO_SNDBUF:
|
||||
if (length != sizeof(uint32))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
socket->send.buffer_size = *(const uint32 *)value;
|
||||
return B_OK;
|
||||
|
||||
case SO_RCVBUF:
|
||||
if (length != sizeof(uint32))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
socket->receive.buffer_size = *(const uint32 *)value;
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ENOPROTOOPT;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
socket_shutdown(net_socket *socket, int direction)
|
||||
{
|
||||
return socket->first_info->shutdown(socket->first_protocol, direction);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
socket_std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
{
|
||||
// initialize the main stack if not done so already
|
||||
module_info *module;
|
||||
return get_module(NET_STARTER_MODULE_NAME, &module);
|
||||
}
|
||||
case B_MODULE_UNINIT:
|
||||
return put_module(NET_STARTER_MODULE_NAME);
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
net_socket_module_info gNetSocketModule = {
|
||||
{
|
||||
NET_SOCKET_MODULE_NAME,
|
||||
0,
|
||||
socket_std_ops
|
||||
},
|
||||
create_socket,
|
||||
socket_close,
|
||||
socket_free,
|
||||
|
||||
socket_readv,
|
||||
socket_writev,
|
||||
socket_control,
|
||||
|
||||
socket_read_avail,
|
||||
socket_send_avail,
|
||||
|
||||
socket_send_data,
|
||||
socket_receive_data,
|
||||
|
||||
// standard socket API
|
||||
socket_accept,
|
||||
socket_bind,
|
||||
socket_connect,
|
||||
socket_getpeername,
|
||||
socket_getsockname,
|
||||
socket_getsockopt,
|
||||
socket_listen,
|
||||
socket_recv,
|
||||
socket_recvfrom,
|
||||
socket_send,
|
||||
socket_sendto,
|
||||
socket_setsockopt,
|
||||
socket_shutdown,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user