xref: /illumos-gate/usr/src/lib/libnsl/nsl/t_rcvv.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1998-2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * t_rcv.c and t_rcvv.c are very similar and contain common code.
31  * Any changes to either of them should be reviewed to see whether they
32  * are applicable to the other file.
33  */
34 #include "mt.h"
35 #include <stdlib.h>
36 #include <rpc/trace.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <stropts.h>
40 #include <sys/stream.h>
41 #define	_SUN_TPI_VERSION 2
42 #include <sys/tihdr.h>
43 #include <sys/timod.h>
44 #include <xti.h>
45 #include <syslog.h>
46 #include <assert.h>
47 #include "tx.h"
48 
49 int
50 _tx_rcvv(int fd, struct t_iovec *tiov, unsigned int tiovcount,  int *flags,
51     int api_semantics)
52 {
53 	struct strbuf ctlbuf, databuf;
54 	int retval, flg = 0;
55 	int msglen;
56 	union T_primitives *pptr;
57 	struct _ti_user *tiptr;
58 	int sv_errno;
59 	int didalloc;
60 	unsigned int nbytes;
61 	char *dataptr;
62 
63 	trace5(TR_t_rcvv, 0, fd, tiov, tiovcount, flags);
64 	if ((tiptr = _t_checkfd(fd, 0, api_semantics)) == NULL) {
65 		sv_errno = errno;
66 		trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
67 		errno = sv_errno;
68 		return (-1);
69 	}
70 	sig_mutex_lock(&tiptr->ti_lock);
71 
72 	if (tiptr->ti_servtype == T_CLTS) {
73 		t_errno = TNOTSUPPORT;
74 		sig_mutex_unlock(&tiptr->ti_lock);
75 		trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
76 		return (-1);
77 	}
78 
79 	assert(api_semantics == TX_XTI_XNS5_API);
80 
81 	if (tiovcount == 0 || tiovcount > T_IOV_MAX) {
82 		t_errno = TBADDATA;
83 		sig_mutex_unlock(&tiptr->ti_lock);
84 		trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
85 		return (-1);
86 	}
87 
88 	if (! (tiptr->ti_state == T_DATAXFER ||
89 		tiptr->ti_state == T_OUTREL)) {
90 		t_errno = TOUTSTATE;
91 		sig_mutex_unlock(&tiptr->ti_lock);
92 		trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
93 		return (-1);
94 	}
95 
96 	/*
97 	 * Check in lookbuf for stuff
98 	 */
99 	if (tiptr->ti_lookcnt > 0) {
100 		/*
101 		 * Implied preference rules give priority to
102 		 * T_DISCON_IND over T_ORDREL_IND. Also certain errors like
103 		 * data received after T_ORDREL_IND or a duplicate T_ORDREL_IND
104 		 * after a T_ORDRELING have priority over TLOOK.
105 		 * This manifests in following code behavior.
106 		 *
107 		 * (1)  If something in lookbuf then check
108 		 *	the stream head also. This may result
109 		 *	in retuning a TLOOK error but only if there are
110 		 *	  - message at stream head but look buffer
111 		 *	    has a T_DISCON_IND event.
112 		 *	  - no messages are on the stream head
113 		 *
114 		 * (2)  If there are messages on the stream head and
115 		 *	all of them are T_ORDREL_IND(i.e. no message in
116 		 *	look buffer is T_DISCON_IND), there
117 		 *	could be data on stream head to be picked up and
118 		 *	we work on the stream head and not return TLOOK.
119 		 *	We remove the event on the stream head and queue it.
120 		 *
121 		 */
122 		do {
123 			retval = _ioctl(fd, I_NREAD, &msglen);
124 		} while (retval < 0 && errno == EINTR);
125 
126 		if (retval < 0) {
127 			sv_errno = errno;
128 			t_errno = TSYSERR;
129 			sig_mutex_unlock(&tiptr->ti_lock);
130 			trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
131 			errno = sv_errno;
132 			return (-1);
133 		}
134 
135 		if (retval > 0) {
136 			/*
137 			 * If any T_DISCON_IND event in look buffer
138 			 * list then return TLOOK. Else continue
139 			 * processing as what could be on the stream
140 			 * head might be a possible T_DISCON_IND (which
141 			 * would have priority over the T_ORDREL_INDs
142 			 * on the look buffer.)
143 			 */
144 			struct _ti_lookbufs *tlbs;
145 
146 			tlbs = &tiptr->ti_lookbufs;
147 			do {
148 				if (*((t_scalar_t *)tlbs->tl_lookcbuf)
149 				    == T_DISCON_IND) {
150 					t_errno = TLOOK;
151 					sig_mutex_unlock(&tiptr->ti_lock);
152 					trace5(TR_t_rcvv, 1, fd, tiov,
153 					    tiovcount, flags);
154 					return (-1);
155 				}
156 			} while ((tlbs = tlbs->tl_next) != NULL);
157 
158 		} else {	/* retval == 0 */
159 			/*
160 			 * Nothing on stream head so whatever in
161 			 * look buffer has nothing that might override
162 			 * it.
163 			 */
164 			t_errno = TLOOK;
165 			sig_mutex_unlock(&tiptr->ti_lock);
166 			trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
167 			return (-1);
168 		}
169 	}
170 
171 	/*
172 	 * Acquire ctlbuf for use in sending/receiving control part
173 	 * of the message.
174 	 */
175 	if (_t_acquire_ctlbuf(tiptr, &ctlbuf, &didalloc) < 0) {
176 		sv_errno = errno;
177 		sig_mutex_unlock(&tiptr->ti_lock);
178 		trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
179 		errno = sv_errno;
180 		return (-1);
181 	}
182 
183 	nbytes = _t_bytecount_upto_intmax(tiov, tiovcount);
184 	dataptr = NULL;
185 	if (nbytes != 0 && ((dataptr = malloc(nbytes)) == NULL)) {
186 		sv_errno = errno;
187 		t_errno = TSYSERR;
188 		if (didalloc)
189 			free(ctlbuf.buf);
190 		else
191 			tiptr->ti_ctlbuf = ctlbuf.buf;
192 		sig_mutex_unlock(&tiptr->ti_lock);
193 		trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
194 		errno = sv_errno;
195 		return (-1);
196 	}
197 
198 	databuf.maxlen = (int)nbytes;
199 	databuf.len = 0;
200 	databuf.buf = dataptr;
201 
202 	*flags = 0;
203 
204 	/*
205 	 * This is a call that may block indefinitely so we drop the
206 	 * lock and allow signals in MT case here and reacquire it.
207 	 * Error case should roll back state changes done above
208 	 * (happens to be no state change here)
209 	 */
210 	sig_mutex_unlock(&tiptr->ti_lock);
211 	if ((retval = getmsg(fd, &ctlbuf, &databuf, &flg)) < 0) {
212 		if (errno == EAGAIN)
213 			t_errno = TNODATA;
214 		else
215 			t_errno = TSYSERR;
216 		sv_errno = errno;
217 		sig_mutex_lock(&tiptr->ti_lock);
218 		errno = sv_errno;
219 		goto err_out;
220 	}
221 	sig_mutex_lock(&tiptr->ti_lock);
222 
223 	assert((retval & MORECTL) == 0); /* MORECTL should not be on */
224 
225 	if (databuf.len == -1) databuf.len = 0;
226 
227 	if (ctlbuf.len > 0) {
228 		if (ctlbuf.len < (int)sizeof (t_scalar_t)) {
229 			t_errno = TSYSERR;
230 			errno = EPROTO;
231 			goto err_out;
232 		}
233 
234 		pptr = (union T_primitives *)ctlbuf.buf;
235 
236 		switch (pptr->type) {
237 
238 		case T_EXDATA_IND:
239 			*flags |= T_EXPEDITED;
240 			if (retval > 0)
241 				tiptr->ti_flags |= EXPEDITED;
242 			/* FALLTHROUGH */
243 		case T_DATA_IND:
244 			/*
245 			 * Uses the fact T_DATA_IND and T_EXDATA_IND
246 			 * are same in size
247 			 */
248 			if ((ctlbuf.len < (int)sizeof (struct T_data_ind)) ||
249 			    (tiptr->ti_lookcnt > 0)) {
250 				/*
251 				 * ti_lookcnt > 0 implies data
252 				 * received after T_DISCON_IND or
253 				 * T_ORDREL_IND hence error
254 				 */
255 				t_errno = TSYSERR;
256 				errno = EPROTO;
257 				goto err_out;
258 			}
259 
260 			if ((pptr->data_ind.MORE_flag) || retval)
261 				*flags |= T_MORE;
262 			if ((pptr->data_ind.MORE_flag) && retval)
263 				tiptr->ti_flags |= MORE;
264 			/*
265 			 * No real state change on T_RCV event (noop)
266 			 *
267 			 * We invoke the macro only for error logging
268 			 * part of its capabilities when in a bad state.
269 			 */
270 			_T_TX_NEXTSTATE(T_RCV, tiptr,
271 					"t_rcvv: invalid state event T_RCV");
272 			if (didalloc)
273 				free(ctlbuf.buf);
274 			else
275 				tiptr->ti_ctlbuf = ctlbuf.buf;
276 			_t_scatter(&databuf, tiov, tiovcount);
277 			if (dataptr != NULL)
278 				free(dataptr);
279 			sig_mutex_unlock(&tiptr->ti_lock);
280 			trace5(TR_t_rcvv, 0, fd, tiov, tiovcount, flags);
281 			return (databuf.len);
282 
283 		case T_ORDREL_IND:
284 			if (tiptr->ti_lookcnt > 0) {
285 				/*
286 				 * ti_lookcnt > 0 implies T_ORDREL_IND
287 				 * received after T_DISCON_IND or
288 				 * another T_ORDREL_IND hence error.
289 				 */
290 				t_errno = TSYSERR;
291 				errno = EPROTO;
292 				goto err_out;
293 			}
294 			/* FALLTHROUGH */
295 		case T_DISCON_IND:
296 			/*
297 			 * Post event (T_ORDREL_IND/T_DISCON_IND) to
298 			 * the lookbuffer list.
299 			 */
300 
301 			if (_t_register_lookevent(tiptr, databuf.buf,
302 					databuf.len,
303 					ctlbuf.buf, ctlbuf.len) < 0) {
304 				t_errno = TSYSERR;
305 				errno = ENOMEM;
306 				goto err_out;
307 			}
308 			/*
309 			 * We know that T_DISCON_IND is stored in
310 			 * last look buffer. If there is more data
311 			 * that follows, we try to append it to
312 			 * the same look buffer
313 			 */
314 			if (retval & MOREDATA) {
315 				ctlbuf.maxlen = 0; /* XXX why ? */
316 				ctlbuf.len = 0;
317 
318 				/*
319 				 * XXX Will break (-ve maxlen) for
320 				 * transport provider with unbounded
321 				 * T_DISCON_IND data part (-1).
322 				 */
323 				databuf.maxlen =
324 					tiptr->ti_rcvsize - databuf.len;
325 
326 				databuf.len = 0;
327 				databuf.buf =
328 					tiptr->ti_lookbufs.tl_lookdbuf +
329 					tiptr->ti_lookbufs.tl_lookdlen;
330 				*flags = 0;
331 
332 				/*
333 				 * Since MOREDATA was set, we assume
334 				 * that this getmsg will not block
335 				 * indefinitely
336 				 */
337 				do {
338 					retval = getmsg(fd, &ctlbuf,
339 							&databuf, &flg);
340 				} while (retval < 0 && errno == EINTR);
341 
342 				if (retval < 0) {
343 					t_errno = TSYSERR;
344 					goto err_out;
345 				}
346 				if (databuf.len == -1) databuf.len = 0;
347 				if (retval > 0) {
348 					/* MORECTL should not be on */
349 					assert((retval & MORECTL) == 0);
350 					/*
351 					 * XXX - Why ?
352 					 * No support for unbounded data
353 					 * on T_DISCON_IND ?
354 					 */
355 					t_errno = TSYSERR;
356 					errno = EPROTO;
357 					goto err_out;
358 				}
359 				tiptr->ti_lookbufs.tl_lookdlen +=
360 					databuf.len;
361 			}
362 
363 			t_errno = TLOOK;
364 			goto err_out;
365 
366 		default:
367 			break;
368 		}
369 
370 		t_errno = TSYSERR;
371 		errno = EPROTO;
372 		goto err_out;
373 
374 	} else {		/* else for "if (ctlbuf.len > 0)" */
375 		if (!retval && (tiptr->ti_flags & MORE)) {
376 			*flags |= T_MORE;
377 			tiptr->ti_flags &= ~MORE;
378 		}
379 		if (retval & MOREDATA)
380 			*flags |= T_MORE;
381 
382 		/*
383 		 * If inside an ETSDU, set expedited flag and turn
384 		 * of internal version when reach end of "ETIDU".
385 		 */
386 		if (tiptr->ti_flags & EXPEDITED) {
387 			*flags |= T_EXPEDITED;
388 			if (!retval)
389 				tiptr->ti_flags &= ~EXPEDITED;
390 		}
391 
392 		/*
393 		 * No real state change on T_RCV events (It is a NOOP)
394 		 *
395 		 * We invoke the macro only for error logging
396 		 * part of its capabilities when in a bad state.
397 		 */
398 		_T_TX_NEXTSTATE(T_RCV, tiptr,
399 			"t_rcvv: state invalid T_RCV event");
400 		if (didalloc)
401 			free(ctlbuf.buf);
402 		else
403 			tiptr->ti_ctlbuf = ctlbuf.buf;
404 		_t_scatter(&databuf, tiov, tiovcount);
405 		if (dataptr != NULL)
406 			free(dataptr);
407 		sig_mutex_unlock(&tiptr->ti_lock);
408 		trace5(TR_t_rcvv, 0, fd, tiov, tiovcount, flags);
409 		return (databuf.len);
410 	}
411 	/* NOTREACHED */
412 
413 err_out:
414 	sv_errno = errno;
415 	if (didalloc)
416 		free(ctlbuf.buf);
417 	else
418 		tiptr->ti_ctlbuf = ctlbuf.buf;
419 	if (dataptr != NULL)
420 		free(dataptr);
421 	sig_mutex_unlock(&tiptr->ti_lock);
422 
423 	trace5(TR_t_rcvv, 1, fd, tiov, tiovcount, flags);
424 	errno = sv_errno;
425 	return (-1);
426 }
427