Alex 'AdUser' Z
9 years ago
2 changed files with 265 additions and 0 deletions
@ -0,0 +1,241 @@
|
||||
/* Copyright 2016 Alex 'AdUser' Z (ad_user@runbox.com)
|
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
*/ |
||||
#include "common.h" |
||||
#include "cmsg.h" |
||||
#include "csocket.h" |
||||
#include "log.h" |
||||
|
||||
int |
||||
f2b_csocket_create(const char *path) { |
||||
struct sockaddr_un addr; |
||||
int csock = -1; |
||||
int flags = -1; |
||||
|
||||
assert(path != NULL); |
||||
|
||||
if ((csock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { |
||||
f2b_log_msg(log_error, "can't create control socket: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
memset(&addr, 0x0, sizeof(addr)); |
||||
addr.sun_family = AF_UNIX; |
||||
strlcpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); |
||||
|
||||
if ((flags = fcntl(csock, F_GETFL, 0)) < 0) |
||||
return -1; |
||||
if (fcntl(csock, F_SETFL, flags | O_NONBLOCK) < 0) { |
||||
f2b_log_msg(log_error, "can't set non-blocking mode on socket: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
unlink(path); |
||||
if (bind(csock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) != 0) { |
||||
f2b_log_msg(log_error, "bind() on socket failed: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
return csock; |
||||
} |
||||
|
||||
void |
||||
f2b_csocket_destroy(int sock, const char *path) { |
||||
assert(path != NULL); |
||||
|
||||
if (sock >= 0) |
||||
close(sock); |
||||
unlink(path); |
||||
|
||||
return; |
||||
} |
||||
|
||||
int |
||||
f2b_csocket_connect(const char *spath, const char *cpath) { |
||||
struct sockaddr_un caddr, saddr; |
||||
int csock = -1; |
||||
|
||||
assert(spath != NULL); |
||||
assert(cpath != NULL); |
||||
|
||||
memset(&saddr, 0x0, sizeof(caddr)); |
||||
memset(&caddr, 0x0, sizeof(saddr)); |
||||
|
||||
caddr.sun_family = AF_LOCAL; |
||||
strlcpy(caddr.sun_path, cpath, sizeof(caddr.sun_path)); |
||||
saddr.sun_family = AF_LOCAL; |
||||
strlcpy(saddr.sun_path, spath, sizeof(saddr.sun_path)); |
||||
|
||||
if ((csock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { |
||||
f2b_log_msg(log_error, "can't create control socket"); |
||||
return -1; |
||||
} |
||||
|
||||
if (bind(csock, (struct sockaddr *) &caddr, sizeof(struct sockaddr_un)) != 0) { |
||||
f2b_log_msg(log_error, "bind() to local side of socket failed: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
if (connect(csock, (struct sockaddr *) &saddr, sizeof(struct sockaddr_un)) != 0) { |
||||
f2b_log_msg(log_error, "connect() to socket failed: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
return csock; |
||||
} |
||||
|
||||
void |
||||
f2b_csocket_disconnect(int sock, const char *cpath) { |
||||
unlink(cpath); |
||||
if (sock >= 0) |
||||
close(sock); |
||||
return; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Recieve and unpack control message |
||||
* @param csock Opened socket fd |
||||
* @param cmsg Control message pointer |
||||
* @param addr Pointer for sender address store |
||||
* @param addrlen Size of address storage |
||||
* @return >0 on success, 0 on no avalilable messages, <0 on error |
||||
*/ |
||||
int |
||||
f2b_csocket_recv(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *addrlen) { |
||||
struct msghdr msg; |
||||
uint16_t size; |
||||
int ret; |
||||
|
||||
assert(csock >= 0); |
||||
assert(cmsg != NULL); |
||||
|
||||
struct iovec iov[] = { |
||||
{ .iov_len = sizeof(cmsg->magic), .iov_base = &cmsg->magic[0] }, |
||||
{ .iov_len = sizeof(cmsg->version), .iov_base = &cmsg->version }, |
||||
{ .iov_len = sizeof(cmsg->type), .iov_base = &cmsg->type }, |
||||
{ .iov_len = sizeof(cmsg->flags), .iov_base = &cmsg->flags }, |
||||
{ .iov_len = sizeof(cmsg->size), .iov_base = &size /* need ntohs */ }, |
||||
{ .iov_len = sizeof(cmsg->pass), .iov_base = &cmsg->pass[0] }, |
||||
{ .iov_len = sizeof(cmsg->data), .iov_base = &cmsg->data[0] }, |
||||
}; |
||||
|
||||
memset(&msg, 0x0, sizeof(msg)); |
||||
msg.msg_name = addr; |
||||
msg.msg_namelen = *addrlen; |
||||
msg.msg_iov = iov; |
||||
msg.msg_iovlen = 7; |
||||
|
||||
ret = recvmsg(csock, &msg, 0); |
||||
if (ret < 0 && errno == EAGAIN) |
||||
return 0; /* non-blocking mode & no messages */ |
||||
if (ret < 0) { |
||||
f2b_log_msg(log_error, "recvmsg(): %s", strerror(errno)); |
||||
return ret; |
||||
} |
||||
if (msg.msg_flags & MSG_TRUNC) { |
||||
f2b_log_msg(log_warn, "damaged cmsg on socket: truncated"); |
||||
return -1; |
||||
} |
||||
if (memcmp(cmsg->magic, "F2B", 3) != 0) { |
||||
f2b_log_msg(log_warn, "damaged cmsg on socket: no magic"); |
||||
return -1; |
||||
} |
||||
if (cmsg->version != F2B_PROTO_VER) { |
||||
f2b_log_msg(log_warn, "damaged cmsg on socket: version mismatch"); |
||||
return -1; |
||||
} |
||||
if (cmsg->type >= CMD_MAX_NUMBER) { |
||||
f2b_log_msg(log_warn, "damaged cmsg on socket: unknown command type"); |
||||
return -1; |
||||
} |
||||
cmsg->size = ntohs(size); |
||||
if (ret != (cmsg->size + 16)) { |
||||
f2b_log_msg(log_warn, "damaged cmsg on socket: expected %u bytes, got %u", cmsg->size + 16, ret); |
||||
return -1; |
||||
} |
||||
*addrlen = msg.msg_namelen; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Pack and send control message |
||||
* @param csock Opened socket fd |
||||
* @param cmsg Control message pointer |
||||
* @param addr Pointer for destination address store |
||||
* @param addrlen Size of address storage |
||||
* @return >0 on success |
||||
*/ |
||||
int |
||||
f2b_csocket_send(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *addrlen) { |
||||
struct msghdr msg; |
||||
uint16_t size; |
||||
int ret; |
||||
|
||||
assert(csock >= 0); |
||||
assert(cmsg != NULL); |
||||
|
||||
struct iovec iov[] = { |
||||
{ .iov_len = sizeof(cmsg->magic), .iov_base = &cmsg->magic[0] }, |
||||
{ .iov_len = sizeof(cmsg->version), .iov_base = &cmsg->version }, |
||||
{ .iov_len = sizeof(cmsg->type), .iov_base = &cmsg->type }, |
||||
{ .iov_len = sizeof(cmsg->flags), .iov_base = &cmsg->flags }, |
||||
{ .iov_len = sizeof(cmsg->size), .iov_base = &size /* need htons */ }, |
||||
{ .iov_len = sizeof(cmsg->pass), .iov_base = &cmsg->pass[0] }, |
||||
{ .iov_len = cmsg->size, .iov_base = &cmsg->data[0] }, |
||||
}; |
||||
size = htons(cmsg->size); |
||||
|
||||
memset(&msg, 0x0, sizeof(msg)); |
||||
msg.msg_name = addr; |
||||
msg.msg_namelen = *addrlen; |
||||
msg.msg_iov = iov; |
||||
msg.msg_iovlen = 7; |
||||
|
||||
if ((ret = sendmsg(csock, &msg, 0)) <= 0) |
||||
f2b_log_msg(log_error, "sendmsg(): %s", strerror(errno)); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/**
|
||||
* @return -1 on error, 0 on no messages, and > 0 on some messages processed |
||||
*/ |
||||
int |
||||
f2b_csocket_poll(int csock, void (*cb)(const f2b_cmsg_t *cmsg, char *res, size_t ressize)) { |
||||
char res[DATA_LEN_MAX + 1]; |
||||
f2b_cmsg_t cmsg; |
||||
struct sockaddr_storage addr; |
||||
socklen_t addrlen; |
||||
int ret, processed = 0; |
||||
|
||||
assert(csock >= 0); |
||||
assert(cb != NULL); |
||||
|
||||
while (1) { |
||||
memset(&cmsg, 0x0, sizeof(cmsg)); |
||||
memset(&addr, 0x0, sizeof(addr)); |
||||
addrlen = sizeof(addr); |
||||
ret = f2b_csocket_recv(csock, &cmsg, &addr, &addrlen); |
||||
if (ret <= 0) |
||||
break; /* no messages or error */ |
||||
/* TODO: check auth */ |
||||
cb(&cmsg, res, sizeof(res)); |
||||
if (cmsg.flags & CMSG_FLAG_NEED_REPLY) { |
||||
memset(&cmsg, 0x0, sizeof(cmsg)); |
||||
strncpy(cmsg.magic, "F2B", sizeof(cmsg.magic)); |
||||
strlcpy(cmsg.data, res, sizeof(cmsg.data)); |
||||
cmsg.version = F2B_PROTO_VER; |
||||
cmsg.type = CMD_RESP; |
||||
cmsg.size = strlen(res); |
||||
cmsg.data[cmsg.size] = '\0'; |
||||
ret = f2b_csocket_send(csock, &cmsg, &addr, &addrlen); |
||||
} |
||||
processed++; |
||||
} |
||||
|
||||
return processed; |
||||
} |
@ -0,0 +1,24 @@
|
||||
/* Copyright 2016 Alex 'AdUser' Z (ad_user@runbox.com)
|
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
*/ |
||||
#ifndef F2B_CSOCKET_H_ |
||||
#define F2B_CSOCKET_H_ |
||||
|
||||
#include <sys/socket.h> |
||||
#include <arpa/inet.h> |
||||
#include <sys/un.h> |
||||
|
||||
int f2b_csocket_create (const char *path); |
||||
void f2b_csocket_destroy(int csock, const char *path); |
||||
|
||||
int f2b_csocket_connect(const char *spath, const char *cpath); |
||||
void f2b_csocket_disconnect(int sock, const char *cpath); |
||||
|
||||
int f2b_csocket_poll(int csock, void (*cb)(const f2b_cmsg_t *cmsg, char *res, size_t ressize)); |
||||
int f2b_csocket_send(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *socklen); |
||||
int f2b_csocket_recv(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *socklen); |
||||
|
||||
#endif /* F2B_CSOCKET_H_ */ |
Loading…
Reference in new issue