在Unix域Socket中为多个客户端分配名称

时间:2017-02-21 19:56:17

标签: c multithreading unix-socket

到目前为止,一切都很完美。我遇到的问题就是我无法想到一种分配方式,或者我会说给连接到服务器的客户端命名,以便其他知道的客户端写了一条消息

服务器:

while (1) {
FD_ZERO(&read_set);
//FD_ZERO(&write_set);

FD_SET(fd, &read_set);
//FD_SET(fd, &write_set);

for (i = 0; i < num_clients; i++) { //at first this part will not excute
  FD_SET(clients[i], &read_set);
}

select(fd + num_clients + 1, &read_set, NULL, NULL, NULL);


if (FD_ISSET(fd, &read_set)) {
  if ( (clients[num_clients++] = accept(fd, NULL, NULL)) == -1) {
    perror("accept error");
    continue;
  }
  printf("we got a connection!\n");
}


for (i = 0; i < num_clients; i++) {

  if (FD_ISSET(clients[i], &read_set)) {
    msg = read(clients[i], buf, sizeof(buf));
    if(msg > 0){
      int savedclnt = clients[i];
      printf("client %d says: %s\n", i, buf);

      for(int p=0;p<num_clients;p++)
      {
        if( clients[p]!= savedclnt){
          //write("from %d",clients[p]);
          //char msg2 = strcat(clients[i],msg);
          write(clients[p],buf,msg);
        }

      }
    }

  }

}

}

1 个答案:

答案 0 :(得分:0)

假设“名称”是指可靠的(提供客户的UID和/或GID),那么有一些方法......

使用不同路径

您可以使用具有不同权限和所有者的不同文件系统路径。这样,只有某些用户或组可以访问某些UNIX域套接字,您可以根据客户端运行的对象来跟踪客户端的用户。这是一个信息网页:https://troydhanson.github.io/network/Unix_domain_sockets.html

该技术实质上意味着为每个用户或每个组提供单独的Unix域套接字,并使用一种机制来限制对该用户或组的每个套接字的访问。第一部分就是这样:我们使用多个套接字绑定到自己的文件,而不是将单个Unix域套接字绑定到文件名。第二部分是处理Unix套接字实现的潜在差异。 online manual page for Unix sockets陈述(除其他外):

  

POSIX没有          制作有关套接字权限效果的任何声明          文件,以及某些系统(例如,较旧的BSD),套接字权限          被忽略了。便携式程序不应该依赖于此功能          安全

为了解决这个问题,不是使用套接字的权限/所有权,而是可以使用套接字封闭目录的权限和所有权。

这是服务器代码的样子,然后为两个用户的预选和硬编码组实现此目的:用户登录“jill”,用户ID为1003(组ID为1003),以及用户登录“bob”,用户ID为1004(组ID为1004):

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <assert.h>
#include <sys/select.h>

struct user
{
    const char* name;
    uid_t uid;
    gid_t gid;
    int fd;
};

struct user users[] = {
    {"jill", 1003, 1003, -1},
    {"bob", 1004, 1004, -1},
};

static int nusers = 2;

static const char sockpath[] = "socket";

static void mk_user_dir(const struct user* u)
{
    if (mkdir(u->name, 0500) == -1 && errno != EEXIST)
    {
        perror("mkdir");
        exit(EXIT_FAILURE);
    }

    if (chown(u->name, u->uid, u->gid) == -1)
    {
        perror("chown");
        exit(EXIT_FAILURE);
    }
}

static void bind_user_sock(const char* dir, const char* path, int sock)
{
    if (chdir(dir) == -1)
    {
        perror("chdir into userdir");
        exit(EXIT_FAILURE);
    }

    if (unlink(path) == -1 && errno != ENOENT)
    {
        perror("unlink socket");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_un addr;
    assert(strlen(path) < sizeof(addr.sun_path));
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (chdir("..") == -1)
    {
        perror("chdir back into parent");
        exit(EXIT_FAILURE);
    }
}

struct session
{
    struct user* user;
    int fd;
};

#define MAX_SESSIONS 10

struct session sessions[MAX_SESSIONS];

static void setup_user(struct user* u)
{
    mk_user_dir(u);

    u->fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (u->fd == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    bind_user_sock(u->name, sockpath, u->fd);

    if (listen(u->fd, 5) == -1)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    fprintf(stderr, "listening on %s/%s\n", u->name, sockpath);
}

int main()
{
    umask(077);

    int nsessions = 0;
    for (int i = 0; i < MAX_SESSIONS; i++)
    {
        sessions[i].user = NULL;
        sessions[i].fd = -1;
    }

    for (int i = 0; i < nusers; i++)
    {
        setup_user(users + i);
    }

    for (;;)
    {
        int nfds = 0;
        fd_set readfds;
        FD_ZERO(&readfds);

        for (int i = 0; i < nusers; i++)
        {
            FD_SET(users[i].fd, &readfds);
            if (nfds < users[i].fd)
                nfds = users[i].fd;
        }
        for (int i = 0; i < MAX_SESSIONS; i++)
        {
            if (sessions[i].fd != -1)
            {
                FD_SET(sessions[i].fd, &readfds);
                if (nfds < sessions[i].fd)
                    nfds = sessions[i].fd;
            }
        }

        nfds++;

        fprintf(stderr, "calling select for descriptors up to %d\n", nfds);
        if (select(nfds, &readfds, 0, 0, 0) == -1)
        {
            perror("select");
            exit(EXIT_FAILURE);
        }

        if (nsessions > 0)
        {
            fprintf(stderr, "checking for session activity for %d sessions\n", nsessions);
            int nclosed = 0;
            for (int i = 0; i < MAX_SESSIONS; i++)
            {
                if (sessions[i].fd != -1 && FD_ISSET(sessions[i].fd, &readfds))
                {
                    char buf[1024];

                    /* handle session activity (from user associated with session) */

                    int nread = read(sessions[i].fd, buf, sizeof(buf));
                    if (nread == -1)
                    {
                        perror("read");
                        if (errno != EBADF)
                        {
                            exit(EXIT_FAILURE);
                        }
                    }

                    if (nread > 0)
                    {
                        /* handle new session data */
                        fprintf(stderr, "read %d bytes from user %s\n", nread, users[i].name);
                    }
                    else
                    {
                        /* client closed session */
                        fprintf(stderr, "closing session for user %s\n", users[i].name);
                        close(sessions[i].fd);
                        sessions[i].fd = -1;
                        sessions[i].user = NULL;
                        nclosed++;
                    }
                }
            }
            nsessions -= nclosed;
        }

        fprintf(stderr, "checking for new session requests for %d users\n", nusers);
        for (int i = 0; i < nusers; i++)
        {
            if (FD_ISSET(users[i].fd, &readfds))
            {
                /* handle new session request from user */
                struct sockaddr_un addr;
                socklen_t addrlen = sizeof(addr);
                int fd = accept(users[i].fd, (struct sockaddr*)&addr, &addrlen);
                if (fd == -1)
                {
                    perror("accept");
                    exit(EXIT_FAILURE);
                }
                fprintf(stderr, "accept: new session requested from user %s\n", users[i].name);

                int accepted = 0;
                for (int j = 0; j < MAX_SESSIONS; j++)
                {
                    if (sessions[j].fd == -1)
                    {
                        sessions[nsessions].fd = fd;
                        sessions[nsessions].user = users + i;
                        nsessions++;
                        accepted = 1;
                        break;
                    }
                }
                if (!accepted)
                {
                    fprintf(stderr, "rejected new session request from %s: out-of-sessions\n", users[i].name);
                    close(fd);
                }
            }
        }
    }
}

使用`getpeereid`(或`LOCAL_PEERCRED`)

虽然我自己没有使用过,但是在一些Unix系统上似乎可以使用的更简单的解决方案是使用getpeerid function

相关问题