517 lines
10 KiB
C
517 lines
10 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <termios.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
|
|
#include <patty/ax25.h>
|
|
|
|
patty_client *patty_client_new(const char *path) {
|
|
patty_client *client;
|
|
struct sockaddr_un addr;
|
|
|
|
if ((client = malloc(sizeof(*client))) == NULL) {
|
|
goto error_malloc_client;
|
|
}
|
|
|
|
if ((client->socks = patty_dict_new()) == NULL) {
|
|
goto error_dict_new;
|
|
}
|
|
|
|
if ((client->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
|
goto error_socket;
|
|
}
|
|
|
|
memset(&addr, '\0', sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
|
|
|
|
if (connect(client->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
goto error_connect;
|
|
}
|
|
|
|
return client;
|
|
|
|
error_connect:
|
|
close(client->fd);
|
|
|
|
error_socket:
|
|
patty_dict_destroy(client->socks);
|
|
|
|
error_dict_new:
|
|
free(client);
|
|
|
|
error_malloc_client:
|
|
return NULL;
|
|
}
|
|
|
|
ssize_t patty_client_read(patty_client *client, void *buf, size_t len) {
|
|
return read(client->fd, buf, len);
|
|
}
|
|
|
|
ssize_t patty_client_write(patty_client *client, const void *buf, size_t len) {
|
|
return write(client->fd, buf, len);
|
|
}
|
|
|
|
static int request_close(patty_client *client,
|
|
int fd) {
|
|
enum patty_client_call call = PATTY_CLIENT_CLOSE;
|
|
|
|
patty_client_close_request request = { fd };
|
|
patty_client_close_response response;
|
|
|
|
size_t len;
|
|
|
|
if (write(client->fd, &call, sizeof(call)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (write(client->fd, &request, sizeof(request)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if ((len = read(client->fd, &response, sizeof(response))) < 0) {
|
|
goto error_io;
|
|
} else if (len != sizeof(response)) {
|
|
errno = EIO;
|
|
|
|
goto error_io;
|
|
}
|
|
|
|
errno = response.eno;
|
|
|
|
return response.ret;
|
|
|
|
error_io:
|
|
return -1;
|
|
}
|
|
|
|
static int destroy_sock(uint32_t key, void *value, void *ctx) {
|
|
patty_client *client = ctx;
|
|
patty_client_sock *sock = value;
|
|
|
|
(void)request_close(client, sock->fd);
|
|
|
|
free(sock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void patty_client_destroy(patty_client *client) {
|
|
close(client->fd);
|
|
|
|
(void)patty_dict_each(client->socks, destroy_sock, client);
|
|
patty_dict_destroy(client->socks);
|
|
|
|
free(client);
|
|
}
|
|
|
|
int patty_client_ping(patty_client *client) {
|
|
int call = PATTY_CLIENT_PING,
|
|
pong;
|
|
|
|
ssize_t len;
|
|
|
|
if ((len = write(client->fd, &call, sizeof(call))) < 0 || len == 0) {
|
|
goto done;
|
|
}
|
|
|
|
if ((len = read(client->fd, &pong, sizeof(pong))) < 0 || len == 0) {
|
|
goto done;
|
|
}
|
|
|
|
return pong;
|
|
|
|
done:
|
|
if (errno == EIO) {
|
|
errno = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int patty_client_socket(patty_client *client,
|
|
int proto,
|
|
int type) {
|
|
enum patty_client_call call = PATTY_CLIENT_SOCKET;
|
|
|
|
patty_client_socket_request request = {
|
|
proto, type
|
|
};
|
|
|
|
patty_client_socket_response response;
|
|
patty_client_sock *sock;
|
|
|
|
int fd;
|
|
struct termios t;
|
|
|
|
if ((sock = malloc(sizeof(*sock))) == NULL) {
|
|
goto error_malloc_sock;
|
|
}
|
|
|
|
if (write(client->fd, &call, sizeof(call)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (write(client->fd, &request, sizeof(request)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (read(client->fd, &response, sizeof(response)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
sock->fd = response.fd;
|
|
|
|
strncpy(sock->path, response.path, sizeof(sock->path));
|
|
|
|
if ((fd = open(sock->path, O_RDWR)) < 0) {
|
|
goto error_open;
|
|
}
|
|
|
|
if (tcgetattr(fd, &t) < 0) {
|
|
goto error_tcgetattr;
|
|
}
|
|
|
|
cfmakeraw(&t);
|
|
|
|
if (tcsetattr(fd, TCSANOW, &t) < 0) {
|
|
goto error_tcsetattr;
|
|
}
|
|
|
|
if (patty_dict_set(client->socks,
|
|
(uint32_t)fd,
|
|
sock) == NULL) {
|
|
goto error_dict_set;
|
|
}
|
|
|
|
errno = response.eno;
|
|
|
|
return fd;
|
|
|
|
error_dict_set:
|
|
error_tcsetattr:
|
|
error_tcgetattr:
|
|
(void)close(fd);
|
|
|
|
error_open:
|
|
(void)request_close(client, sock->fd);
|
|
|
|
error_io:
|
|
free(sock);
|
|
|
|
error_malloc_sock:
|
|
return -1;
|
|
}
|
|
|
|
int patty_client_setsockopt(patty_client *client,
|
|
int fd,
|
|
int opt,
|
|
void *data,
|
|
size_t len) {
|
|
enum patty_client_call call = PATTY_CLIENT_SETSOCKOPT;
|
|
|
|
patty_client_setsockopt_request request = {
|
|
.opt = opt,
|
|
.len = len
|
|
};
|
|
|
|
patty_client_setsockopt_response response;
|
|
|
|
patty_client_sock *sock;
|
|
|
|
if ((sock = patty_dict_get(client->socks, (uint32_t)fd)) == NULL) {
|
|
errno = EBADF;
|
|
|
|
goto error_dict_get;
|
|
}
|
|
|
|
request.fd = sock->fd;
|
|
|
|
if (write(client->fd, &call, sizeof(call)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (write(client->fd, &request, sizeof(request)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (write(client->fd, data, len) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (read(client->fd, &response, sizeof(response)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
errno = response.eno;
|
|
|
|
return response.ret;
|
|
|
|
error_io:
|
|
error_dict_get:
|
|
return -1;
|
|
}
|
|
|
|
int patty_client_bind(patty_client *client,
|
|
int fd,
|
|
patty_ax25_addr *addr) {
|
|
enum patty_client_call call = PATTY_CLIENT_BIND;
|
|
|
|
patty_client_bind_request request;
|
|
patty_client_bind_response response;
|
|
|
|
patty_client_sock *sock;
|
|
|
|
if ((sock = patty_dict_get(client->socks, (uint32_t)fd)) == NULL) {
|
|
errno = EBADF;
|
|
|
|
goto error_dict_get;
|
|
}
|
|
|
|
memset(&request, '\0', sizeof(request));
|
|
|
|
request.fd = sock->fd;
|
|
|
|
memcpy(&request.addr, addr, sizeof(*addr));
|
|
|
|
if (write(client->fd, &call, sizeof(call)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (write(client->fd, &request, sizeof(request)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (read(client->fd, &response, sizeof(response)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
errno = response.eno;
|
|
|
|
return response.ret;
|
|
|
|
error_io:
|
|
error_dict_get:
|
|
return -1;
|
|
}
|
|
|
|
int patty_client_listen(patty_client *client,
|
|
int fd) {
|
|
enum patty_client_call call = PATTY_CLIENT_LISTEN;
|
|
|
|
patty_client_listen_request request;
|
|
patty_client_listen_response response;
|
|
|
|
patty_client_sock *sock;
|
|
|
|
if ((sock = patty_dict_get(client->socks, (uint32_t)fd)) == NULL) {
|
|
errno = EBADF;
|
|
|
|
goto error_dict_get;
|
|
}
|
|
|
|
request.fd = sock->fd;
|
|
|
|
if (write(client->fd, &call, sizeof(call)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (write(client->fd, &request, sizeof(request)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (read(client->fd, &response, sizeof(response)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
errno = response.eno;
|
|
|
|
return response.ret;
|
|
|
|
error_io:
|
|
error_dict_get:
|
|
return -1;
|
|
}
|
|
|
|
int patty_client_accept(patty_client *client,
|
|
int fd,
|
|
patty_ax25_addr *peer) {
|
|
enum patty_client_call call = PATTY_CLIENT_ACCEPT;
|
|
|
|
patty_client_accept_request request;
|
|
patty_client_accept_response response;
|
|
patty_client_accept_message message;
|
|
|
|
patty_client_sock *local,
|
|
*remote;
|
|
|
|
int pty;
|
|
struct termios t;
|
|
|
|
if ((local = patty_dict_get(client->socks, (uint32_t)fd)) == NULL) {
|
|
errno = EBADF;
|
|
|
|
goto error_dict_get;
|
|
}
|
|
|
|
if ((remote = malloc(sizeof(*remote))) == NULL) {
|
|
goto error_malloc_remote;
|
|
}
|
|
|
|
request.fd = local->fd;
|
|
|
|
memset(&response, '\0', sizeof(response));
|
|
|
|
if (write(client->fd, &call, sizeof(call)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (write(client->fd, &request, sizeof(request)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
/*
|
|
* First, the server will tell us if the fd specified in accept() is indeed
|
|
* accepting connections.
|
|
*/
|
|
if (read(client->fd, &response, sizeof(response)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (response.ret < 0) {
|
|
errno = response.eno;
|
|
|
|
return response.ret;
|
|
}
|
|
|
|
/*
|
|
* Next, we will wait for the server to receive a SABM or SABME frame, and
|
|
* notify us via the listening socket that a connection has been accepted.
|
|
*/
|
|
if (read(fd, &message, sizeof(message)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
remote->fd = message.fd;
|
|
|
|
memcpy(peer, &message.peer, sizeof(*peer));
|
|
strncpy(remote->path, message.path, sizeof(remote->path));
|
|
|
|
if ((pty = open(remote->path, O_RDWR)) < 0) {
|
|
goto error_open;
|
|
}
|
|
|
|
if (tcgetattr(pty, &t) < 0) {
|
|
goto error_tcgetattr;
|
|
}
|
|
|
|
cfmakeraw(&t);
|
|
|
|
if (tcsetattr(pty, TCSANOW, &t) < 0) {
|
|
goto error_tcsetattr;
|
|
}
|
|
|
|
if (patty_dict_set(client->socks,
|
|
(uint32_t)pty,
|
|
remote) == NULL) {
|
|
goto error_dict_set;
|
|
}
|
|
|
|
errno = response.eno;
|
|
|
|
return pty;
|
|
|
|
error_tcsetattr:
|
|
error_tcgetattr:
|
|
error_dict_set:
|
|
(void)close(pty);
|
|
|
|
error_open:
|
|
(void)request_close(client, remote->fd);
|
|
|
|
error_io:
|
|
free(remote);
|
|
|
|
error_malloc_remote:
|
|
error_dict_get:
|
|
return -1;
|
|
}
|
|
|
|
int patty_client_connect(patty_client *client,
|
|
int fd,
|
|
patty_ax25_addr *peer) {
|
|
enum patty_client_call call = PATTY_CLIENT_CONNECT;
|
|
|
|
patty_client_connect_request request;
|
|
patty_client_connect_response response;
|
|
|
|
patty_client_sock *sock;
|
|
|
|
if ((sock = patty_dict_get(client->socks, (uint32_t)fd)) == NULL) {
|
|
errno = EBADF;
|
|
|
|
goto error_dict_get;
|
|
}
|
|
|
|
memset(&request, '\0', sizeof(request));
|
|
|
|
request.fd = sock->fd;
|
|
|
|
memcpy(&request.peer, peer, sizeof(*peer));
|
|
|
|
if (write(client->fd, &call, sizeof(call)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (write(client->fd, &request, sizeof(request)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
if (read(client->fd, &response, sizeof(response)) < 0) {
|
|
goto error_io;
|
|
}
|
|
|
|
errno = response.eno;
|
|
|
|
return response.ret;
|
|
|
|
error_io:
|
|
error_dict_get:
|
|
return -1;
|
|
}
|
|
|
|
int patty_client_close(patty_client *client,
|
|
int fd) {
|
|
patty_client_sock *sock;
|
|
|
|
if ((sock = patty_dict_get(client->socks, (uint32_t)fd)) == NULL) {
|
|
errno = EBADF;
|
|
|
|
goto error_dict_get;
|
|
}
|
|
|
|
if (request_close(client, sock->fd) < 0) {
|
|
goto error_request_close;
|
|
}
|
|
|
|
if (close(fd) < 0) {
|
|
goto error_close;
|
|
}
|
|
|
|
patty_dict_delete(client->socks, (uint32_t)fd);
|
|
|
|
free(sock);
|
|
|
|
return 0;
|
|
|
|
error_close:
|
|
error_request_close:
|
|
error_dict_get:
|
|
return -1;
|
|
}
|