Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <sys/epoll.h>
- #include <netdb.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string>
- #include <signal.h>
- #include <queue>
- #include <map>
- #include <set>
- class SOCKETS {
- public:
- SOCKETS() {}
- ~SOCKETS() {deinit();}
- static const int EPOLL_MAX_EVENTS;
- inline bool init(std::string *error) {
- events = nullptr;
- sfd = -1;
- efd = -1;
- if (sigfillset(&sigset_all) == -1) {
- if (error) error->append("sigfillset failed\n");
- return false;
- }
- if (sigemptyset(&sigset_none) == -1) {
- if (error) error->append("sigemptyset failed\n");
- return false;
- }
- return true;
- }
- inline bool open(const char *port, std::string *error) {
- /*static struct sockaddr_in sa_zero;
- struct sockaddr_in sa;
- int x = 1;
- int fd;
- if ( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) {
- printf( "Init_tcp_socket: socket" );
- exit( 1 );
- }
- if ( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,(char *) &x, sizeof(x) ) < 0 ) {
- printf( "Init_tcp_socket: SO_REUSEADDR" );
- ::close(fd);
- exit( 1 );
- }
- int ppp = atoi(port);
- sa = sa_zero;
- sa.sin_family = AF_INET;
- sa.sin_port = htons( ppp );
- if ( bind( fd, (struct sockaddr *) &sa, sizeof(sa) ) < 0 ) {
- printf("Init socket: bind (port %d)", ppp );
- ::close(fd);
- exit(1);
- }
- sfd = fd;*/
- // Uncomment code above and comment out the next line to make SO_REUSEADDR work.
- sfd = create_and_bind(port, error);
- if (sfd == -1) return false;
- s = make_socket_non_blocking(sfd, error);
- if (s == -1) return false;
- s = ::listen(sfd, SOMAXCONN);
- if (s == -1) return false;
- efd = epoll_create1(0);
- if (efd == -1) return false;
- event.data.fd = sfd;
- event.events = EPOLLIN|EPOLLET;
- s = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
- if (s == -1) return false;
- // Buffer where events are returned:
- events = (struct epoll_event *) calloc(EPOLL_MAX_EVENTS, sizeof event);
- if (events == nullptr) {
- if (error) error->append("unable to allocate memory for events\n");
- return false;
- }
- return true;
- }
- inline bool close(std::string *error) {
- if (sfd == -1) return false;
- if (!close(sfd, error)) return false;
- return true;
- }
- inline void disconnect(std::string *error) {
- for (auto d : descriptors) {
- if (disconnections.count(d) > 0) continue;
- disconnections.insert(d);
- if (shutdown(d, SHUT_WR) == -1) {
- if (error) {
- char buf[64];
- sprintf(buf, "shutdown(%d, SHUT_WR): ", d);
- error->append(buf);
- error->append(strerror(errno));
- error->append("\n");
- }
- }
- }
- }
- inline bool wait(std::string *error) {
- if (efd == -1 || !events) {
- if (error) error->append("sockets uninitialized\n");
- return false;
- }
- if (!new_descs.empty()) {
- if (error) error->append("new descriptor queue full\n");
- return false;
- }
- if (!del_descs.empty()) {
- if (error) error->append("deleted descriptor queue full\n");
- return false;
- }
- int n, i;
- if ((n = epoll_pwait(efd, events, EPOLL_MAX_EVENTS, -1, &sigset_none)) == -1) {
- if (errno == EINTR) goto Disconnect;
- if (error) {
- error->append("epoll_pwait: ");
- error->append(strerror(errno));
- error->append("\n");
- }
- return false;
- }
- new_hosts.clear();
- new_ports.clear();
- new_descs = {};
- del_descs = {};
- for (i=0; i<n; i++) {
- if (( events[i].events & EPOLLERR )
- // || ( events[i].events & EPOLLHUP )
- || (!(events[i].events & EPOLLIN) )) {
- int d = events[i].data.fd;
- // An error has occured on this fd, or the socket is not
- // ready for reading (why were we notified then?)
- if (error) {
- char buf[64];
- sprintf(buf, "epoll error on descriptor %d\n", d);
- error->append(buf);
- }
- if (!close(d, error)); return false;
- continue;
- }
- else if (sfd == events[i].data.fd) {
- // We have a notification on the listening socket, which means one or more incoming connections.
- while (1) {
- struct sockaddr in_addr;
- socklen_t in_len;
- int infd;
- char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
- in_len = sizeof in_addr;
- infd = accept(sfd, &in_addr, &in_len);
- if (infd == -1) {
- if ((errno == EAGAIN )
- || (errno == EWOULDBLOCK)) {
- break; // We have processed all incoming connections.
- }
- else {
- error->append("accept: ");
- error->append(strerror(errno));
- error->append("\n");
- break;
- }
- }
- s = getnameinfo(&in_addr, in_len, hbuf, sizeof hbuf, sbuf, sizeof sbuf, NI_NUMERICHOST|NI_NUMERICSERV);
- if (s == 0) {
- new_hosts[infd] = hbuf;
- new_ports[infd] = sbuf;
- }
- else {
- error->append("getnameinfo: ");
- error->append(gai_strerror(s));
- error->append("\n");
- }
- // Make the incoming socket non-blocking and add it to the list of fds to monitor.
- s = make_socket_non_blocking(infd, error);
- if (s == -1) return false;
- event.data.fd = infd;
- event.events = EPOLLIN|EPOLLET;
- s = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
- if (s == -1) {
- error->append("epoll_ctl: ");
- error->append(strerror(errno));
- error->append("\n");
- return false;
- }
- new_descs.push(infd);
- descriptors.insert(infd);
- }
- continue;
- }
- else {
- // We have data on the fd waiting to be read. Read and
- // display it. We must read whatever data is available
- // completely, as we are running in edge-triggered mode
- // and won't get a notification again for the same
- // data.
- int done = 0;
- while (1) {
- ssize_t count;
- char buf[512];
- count = read(events[i].data.fd, buf, sizeof buf);
- if (count == -1) {
- // If errno == EAGAIN, that means we have read all data. So go back to the main loop.
- if (errno != EAGAIN) {
- error->append("read: ");
- error->append(strerror(errno));
- error->append("\n");
- done = 1;
- }
- break;
- }
- else if (count == 0) {
- // End of file. The remote has closed the connection.
- done = 1;
- break;
- }
- // Write the buffer to standard output.
- s = write(1, buf, count);
- if (s == -1) {
- error->append("write: ");
- error->append(strerror(errno));
- error->append("\n");
- return false;
- }
- }
- if (done) {
- int fd = events[i].data.fd;
- // Closing the descriptor will make epoll remove it
- // from the set of descriptors which are monitored.
- if (!close(fd, error)) {
- return false;
- }
- }
- }
- }
- Disconnect:
- while (!disconnections.empty()) {
- int d = *disconnections.begin();
- disconnections.erase(d);
- if (descriptors.count(d) > 0) {
- if (!close(d, error)) return false;
- del_descs.push(d);
- descriptors.erase(d);
- }
- }
- return true;
- }
- inline bool send_to_desc(int d, const char *text, std::string *error) {
- size_t len = strlen(text);
- if (len == 0) return true;
- s = write(d, text, len);
- if (s == -1) {
- if (error) {
- error->append("write: ");
- error->append(strerror(errno));
- error->append("\n");
- return false;
- }
- }
- return true;
- }
- inline int get_new_desc(std::string *host, std::string *port) {
- if (new_descs.empty()) return -1;
- int d = new_descs.front();
- new_descs.pop();
- if (host != nullptr) {
- if (new_hosts.find(d) != new_hosts.end()) {
- host->assign(new_hosts[d]);
- new_hosts.erase(d);
- }
- else host->assign("unknown");
- }
- if (port != nullptr) {
- if (new_ports.find(d) != new_ports.end()) {
- port->assign(new_ports[d]);
- new_ports.erase(d);
- }
- else port->assign("unknown");
- }
- return d;
- }
- inline int get_del_desc() {
- if (del_descs.empty()) return -1;
- int d = del_descs.front();
- del_descs.pop();
- return d;
- }
- private:
- int sfd, s;
- int efd;
- struct epoll_event event;
- struct epoll_event *events;
- sigset_t sigset_all;
- sigset_t sigset_none;
- sigset_t sigset_orig;
- std::queue<int> new_descs; // New descriptors.
- std::queue<int> del_descs; // Deleted descriptors.
- std::map<int, std::string> new_hosts;
- std::map<int, std::string> new_ports;
- std::set<int> descriptors;
- std::set<int> disconnections; // Descriptors to be disconnected.
- inline void deinit() {
- if (events) {
- free(events);
- events=nullptr;
- }
- }
- inline bool close(int fd, std::string *error) {
- // Blocking all signals before ::close will guarantee
- // that it will not fail having errno set to EINTR.
- printf("Closing descriptor %d.\n", fd);
- if (sigprocmask(SIG_SETMASK, &sigset_all, &sigset_orig) == -1) {
- if (error) {
- error->append("sigprocmask: ");
- error->append(strerror(errno));
- error->append("\n");
- }
- return false;
- }
- int c = ::close(fd);
- int e = errno;
- if (sigprocmask(SIG_SETMASK, &sigset_orig, nullptr) == -1) {
- if (error) {
- error->append("sigprocmask: ");
- error->append(strerror(errno));
- error->append("\n");
- }
- return false;
- }
- if (c == -1) {
- if (error) {
- error->append("close: ");
- error->append(strerror(e));
- error->append("\n");
- }
- if (e != EINTR) return false;
- }
- if (descriptors.count(fd) > 0) {
- del_descs.push(fd);
- descriptors.erase(fd);
- disconnections.erase(fd);
- }
- return true;
- }
- inline int create_and_bind(const char *port, std::string *error) {
- struct addrinfo hints;
- struct addrinfo *result, *rp;
- int s, sfd, x;
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC; // Return IPv4 and IPv6 choices
- hints.ai_socktype = SOCK_STREAM; // We want a TCP socket
- hints.ai_flags = AI_PASSIVE; // All interfaces
- s = getaddrinfo(nullptr, port, &hints, &result);
- if (s != 0) {
- if (error) {
- error->append("getaddrinfo: ");
- error->append(gai_strerror(s));
- error->append("\n");
- }
- return -1;
- }
- for (rp = result; rp != nullptr; rp = rp->ai_next) {
- sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (sfd == -1) continue;
- if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,(char *) &x, sizeof(x)) == -1) {
- if (error) {
- error->append("setsockopt: ");
- error->append(strerror(errno));
- error->append("\n");
- }
- }
- else {
- s = bind(sfd, rp->ai_addr, rp->ai_addrlen);
- if (s == 0) break; // We managed to bind successfully!
- if (error) {
- char buf[64];
- sprintf(buf, "bind(%d, ?, %d): ", sfd, rp->ai_addrlen);
- error->append(buf);
- error->append(strerror(errno));
- error->append("\n");
- }
- }
- if (!close(sfd, error)) {
- sfd = -1;
- goto Fail;
- }
- }
- if (rp == nullptr) sfd = -1;
- Fail:
- freeaddrinfo(result);
- return sfd;
- }
- inline int make_socket_non_blocking(int sfd, std::string *error) {
- int flags, s;
- flags = fcntl(sfd, F_GETFL, 0);
- if (flags == -1) {
- if (error) {
- error->append("fcntl: ");
- error->append(strerror(errno));
- error->append("\n");
- }
- return -1;
- }
- flags |= O_NONBLOCK;
- s = fcntl (sfd, F_SETFL, flags);
- if (s == -1) {
- error->append("fcntl: ");
- error->append(strerror(errno));
- error->append("\n");
- return -1;
- }
- return 0;
- }
- };
- const int SOCKETS::EPOLL_MAX_EVENTS = 64;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement