File: proxy_unix.c

package info (click to toggle)
libnetconf2 4.2.6-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 3,600 kB
  • sloc: ansic: 37,479; xml: 437; sh: 49; makefile: 20
file content (184 lines) | stat: -rw-r--r-- 4,444 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/**
 * @file proxy_unix.c
 * @author Michal Vasko <mvasko@cesnet.cz>
 * @brief libnetconf2 UNIX proxy functions
 *
 * @copyright
 * Copyright (c) 2025 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */
#define _GNU_SOURCE

#include "proxy_unix.h"

#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

#include "compat.h"
#include "config.h"
#include "log_p.h"
#include "session_p.h"

API int
nc_proxy_unix_connect(const char *address, const char *username)
{
    struct sockaddr_un sun;
    struct passwd *pw, pw_buf;
    int sock = -1;
    char *buf = NULL;
    size_t buf_size = 0;

    /* connect to the UNIX socket */
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
        ERR(NULL, "Failed to create socket (%s).", strerror(errno));
        goto error;
    }

    memset(&sun, 0, sizeof(sun));
    sun.sun_family = AF_UNIX;
    snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);

    if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
        ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
        goto error;
    }

    if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
        ERR(NULL, "fcntl failed (%s).", strerror(errno));
        goto error;
    }

    /* NETCONF username */
    if (!username) {
        pw = nc_getpw(geteuid(), NULL, &pw_buf, &buf, &buf_size);
        if (!pw) {
            ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
            goto error;
        }
        username = pw->pw_name;
    }

    /* connect UNIX session */
    if (nc_connect_unix_session(NULL, sock, username, NC_TRANSPORT_TIMEOUT) != 1) {
        goto error;
    }

    free(buf);
    return sock;

error:
    if (sock > -1) {
        close(sock);
    }
    free(buf);
    return -1;
}

API int
nc_proxy_read_msg(int fd, NC_PROT_VERSION version, int timeout_ms, char **buf, uint32_t *buf_len)
{
    int r;
    struct pollfd fds;
    struct nc_session sess = {0};

    /* poll */
    fds.fd = fd;
    fds.events = POLLIN;
    fds.revents = 0;

    r = nc_poll(&fds, 1, timeout_ms);
    if (r < 0) {
        /* error */
        ERR(NULL, "poll error (%s).", strerror(errno));
        return -1;
    } else if (r == 0) {
        /* timeout */
        return 0;
    } else {
        /* socket error */
        if (fds.revents & POLLERR) {
            ERR(NULL, "Communication channel error.");
            return -1;
        }

        /* some poll() implementations may return POLLHUP|POLLIN when the other
         * side has closed but there is data left to read in the buffer */
        if ((fds.revents & POLLHUP) && !(fds.revents & POLLIN)) {
            ERR(NULL, "Communication channel unexpectedly closed.");
            return -1;
        }
    }

    /* fill dummy session (id 0 causes session not to be included in log messages) */
    sess.status = NC_STATUS_RUNNING;
    sess.version = version;
    sess.ti_type = NC_TI_UNIX;
    sess.ti.unixsock.sock = fd;

    /* read a message */
    r = nc_read_msg_io(&sess, 0, 1, buf, buf_len);
    switch (r) {
    case -2:
        ERR(NULL, "Malformed message received.");
        return -1;
    case -1:
        /* error printed */
        return -1;
    case 0:
        /* timeout */
        return 0;
    default:
        /* success */
        break;
    }

    return r;
}

API int
nc_proxy_write_msg(int fd, NC_PROT_VERSION version, const char *buf, uint32_t buf_len)
{
    struct nc_session sess = {0};
    struct nc_wclb_arg warg = {.session = &sess};

    /* fill dummy session (id 0 causes session not to be included in log messages) */
    sess.status = NC_STATUS_RUNNING;
    sess.version = version;
    sess.ti_type = NC_TI_UNIX;
    sess.ti.unixsock.sock = fd;

    /* write the whole message */
    if (nc_write_clb(&warg, buf, buf_len, 0) == -1) {
        return -1;
    }

    /* flush buffer writing the final end tag */
    if (nc_write_clb(&warg, NULL, 0, 0) == -1) {
        return -1;
    }

    return buf_len;
}

API int
nc_proxy_unix_close(int fd)
{
    if (fd < 0) {
        return 0;
    }

    /* just close the socket */
    return close(fd);
}