#include #include #include #include #include #include #include #include #include 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; } 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_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_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; } if (read(client->fd, &response, sizeof(response)) < 0) { goto error_io; } if (response.fd >= 0) { memcpy(peer, &response.peer, sizeof(*peer)); remote->fd = response.fd; strncpy(remote->path, response.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; }