1 /*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: src/usr.sbin/nscd/nscdcli.c,v 1.5 2008/10/23 00:28:21 delphij Exp $
27 */
28
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/event.h>
32 #include <sys/uio.h>
33 #include <sys/un.h>
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
41
42 #include "debug.h"
43 #include "nscdcli.h"
44 #include "protocol.h"
45
46 #define DEFAULT_NSCD_IO_TIMEOUT 4
47
48 static int safe_write(struct nscd_connection_ *, const void *, size_t);
49 static int safe_read(struct nscd_connection_ *, void *, size_t);
50 static int send_credentials(struct nscd_connection_ *, int);
51
52 static int
safe_write(struct nscd_connection_ * connection,const void * data,size_t data_size)53 safe_write(struct nscd_connection_ *connection, const void *data,
54 size_t data_size)
55 {
56 struct kevent eventlist;
57 int nevents;
58 size_t result;
59 ssize_t s_result;
60 struct timespec timeout;
61
62 if (data_size == 0)
63 return (0);
64
65 timeout.tv_sec = DEFAULT_NSCD_IO_TIMEOUT;
66 timeout.tv_nsec = 0;
67 result = 0;
68 do {
69 nevents = kevent(connection->write_queue, NULL, 0, &eventlist,
70 1, &timeout);
71 if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
72 s_result = write(connection->sockfd, data + result,
73 eventlist.data < data_size - result ?
74 eventlist.data : data_size - result);
75 if (s_result == -1)
76 return (-1);
77 else
78 result += s_result;
79
80 if (eventlist.flags & EV_EOF)
81 return (result < data_size ? -1 : 0);
82 } else
83 return (-1);
84 } while (result < data_size);
85
86 return (0);
87 }
88
89 static int
safe_read(struct nscd_connection_ * connection,void * data,size_t data_size)90 safe_read(struct nscd_connection_ *connection, void *data, size_t data_size)
91 {
92 struct kevent eventlist;
93 size_t result;
94 ssize_t s_result;
95 struct timespec timeout;
96 int nevents;
97
98 if (data_size == 0)
99 return (0);
100
101 timeout.tv_sec = DEFAULT_NSCD_IO_TIMEOUT;
102 timeout.tv_nsec = 0;
103 result = 0;
104 do {
105 nevents = kevent(connection->read_queue, NULL, 0, &eventlist, 1,
106 &timeout);
107 if ((nevents == 1) && (eventlist.filter == EVFILT_READ)) {
108 s_result = read(connection->sockfd, data + result,
109 eventlist.data <= data_size - result ? eventlist.data :
110 data_size - result);
111 if (s_result == -1)
112 return (-1);
113 else
114 result += s_result;
115
116 if (eventlist.flags & EV_EOF)
117 return (result < data_size ? -1 : 0);
118 } else
119 return (-1);
120 } while (result < data_size);
121
122 return (0);
123 }
124
125 static int
send_credentials(struct nscd_connection_ * connection,int type)126 send_credentials(struct nscd_connection_ *connection, int type)
127 {
128 struct kevent eventlist;
129 int nevents;
130 ssize_t result;
131 int res;
132
133 struct msghdr cred_hdr;
134 struct iovec iov;
135
136 struct {
137 struct cmsghdr hdr;
138 struct cmsgcred creds;
139 } cmsg;
140
141 TRACE_IN(send_credentials);
142 memset(&cmsg, 0, sizeof(cmsg));
143 cmsg.hdr.cmsg_len = sizeof(cmsg);
144 cmsg.hdr.cmsg_level = SOL_SOCKET;
145 cmsg.hdr.cmsg_type = SCM_CREDS;
146
147 memset(&cred_hdr, 0, sizeof(struct msghdr));
148 cred_hdr.msg_iov = &iov;
149 cred_hdr.msg_iovlen = 1;
150 cred_hdr.msg_control = &cmsg;
151 cred_hdr.msg_controllen = sizeof(cmsg);
152
153 iov.iov_base = &type;
154 iov.iov_len = sizeof(int);
155
156 EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
157 NOTE_LOWAT, sizeof(int), NULL);
158 res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
159
160 nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL);
161 if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
162 result = (sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? -1
163 : 0;
164 EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
165 0, 0, NULL);
166 kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
167 TRACE_OUT(send_credentials);
168 return (result);
169 } else {
170 TRACE_OUT(send_credentials);
171 return (-1);
172 }
173 }
174
175 struct nscd_connection_ *
open_nscd_connection__(struct nscd_connection_params const * params)176 open_nscd_connection__(struct nscd_connection_params const *params)
177 {
178 struct nscd_connection_ *retval;
179 struct kevent eventlist;
180 struct sockaddr_un client_address;
181 int client_address_len, client_socket;
182 int res;
183
184 TRACE_IN(open_nscd_connection);
185 assert(params != NULL);
186
187 client_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
188 client_address.sun_family = PF_LOCAL;
189 strlcpy(client_address.sun_path, params->socket_path,
190 sizeof(client_address.sun_path));
191 client_address_len = sizeof(client_address.sun_family) +
192 strlen(client_address.sun_path) + 1;
193
194 res = connect(client_socket, (struct sockaddr *)&client_address,
195 client_address_len);
196 if (res == -1) {
197 close(client_socket);
198 TRACE_OUT(open_nscd_connection);
199 return (NULL);
200 }
201 fcntl(client_socket, F_SETFL, O_NONBLOCK);
202
203 retval = calloc(1, sizeof(struct nscd_connection_));
204 assert(retval != NULL);
205
206 retval->sockfd = client_socket;
207
208 retval->write_queue = kqueue();
209 assert(retval->write_queue != -1);
210
211 EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD,
212 0, 0, NULL);
213 res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL);
214
215 retval->read_queue = kqueue();
216 assert(retval->read_queue != -1);
217
218 EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD,
219 0, 0, NULL);
220 res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL);
221
222 TRACE_OUT(open_nscd_connection);
223 return (retval);
224 }
225
226 void
close_nscd_connection__(struct nscd_connection_ * connection)227 close_nscd_connection__(struct nscd_connection_ *connection)
228 {
229
230 TRACE_IN(close_nscd_connection);
231 assert(connection != NULL);
232
233 close(connection->sockfd);
234 close(connection->read_queue);
235 close(connection->write_queue);
236 free(connection);
237 TRACE_OUT(close_nscd_connection);
238 }
239
240 int
nscd_transform__(struct nscd_connection_ * connection,const char * entry_name,int transformation_type)241 nscd_transform__(struct nscd_connection_ *connection,
242 const char *entry_name, int transformation_type)
243 {
244 size_t name_size;
245 int error_code;
246 int result;
247
248 TRACE_IN(nscd_transform);
249
250 error_code = -1;
251 result = 0;
252 result = send_credentials(connection, CET_TRANSFORM_REQUEST);
253 if (result != 0)
254 goto fin;
255
256 if (entry_name != NULL)
257 name_size = strlen(entry_name);
258 else
259 name_size = 0;
260
261 result = safe_write(connection, &name_size, sizeof(size_t));
262 if (result != 0)
263 goto fin;
264
265 result = safe_write(connection, &transformation_type, sizeof(int));
266 if (result != 0)
267 goto fin;
268
269 if (entry_name != NULL) {
270 result = safe_write(connection, entry_name, name_size);
271 if (result != 0)
272 goto fin;
273 }
274
275 result = safe_read(connection, &error_code, sizeof(int));
276 if (result != 0)
277 error_code = -1;
278
279 fin:
280 TRACE_OUT(nscd_transform);
281 return (error_code);
282 }
283