xref: /illumos-gate/usr/src/lib/libnsl/nsl/t_rcv.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 1993-2003 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.7 */
33 
34 /*
35  * t_rcv.c and t_rcvv.c are very similar and contain common code.
36  * Any changes to either of them should be reviewed to see whether they
37  * are applicable to the other file.
38  */
39 #include "mt.h"
40 #include <stdlib.h>
41 #include <rpc/trace.h>
42 #include <errno.h>
43 #include <unistd.h>
44 #include <stropts.h>
45 #include <sys/stream.h>
46 #define	_SUN_TPI_VERSION 2
47 #include <sys/tihdr.h>
48 #include <sys/timod.h>
49 #include <xti.h>
50 #include <syslog.h>
51 #include <assert.h>
52 #include "tx.h"
53 
54 int
55 _tx_rcv(int fd, char *buf, unsigned nbytes, int *flags, int api_semantics)
56 {
57 	struct strbuf ctlbuf, databuf;
58 	int retval, flg = 0;
59 	int msglen;
60 	union T_primitives *pptr;
61 	struct _ti_user *tiptr;
62 	int sv_errno;
63 	int didalloc;
64 
65 	trace3(TR_t_rcv, 0, fd, nbytes);
66 	if ((tiptr = _t_checkfd(fd, 0, api_semantics)) == NULL) {
67 		sv_errno = errno;
68 		trace3(TR_t_rcv, 1, fd, nbytes);
69 		errno = sv_errno;
70 		return (-1);
71 	}
72 	sig_mutex_lock(&tiptr->ti_lock);
73 
74 	if (tiptr->ti_servtype == T_CLTS) {
75 		t_errno = TNOTSUPPORT;
76 		sig_mutex_unlock(&tiptr->ti_lock);
77 		trace3(TR_t_rcv, 1, fd, nbytes);
78 		return (-1);
79 	}
80 
81 	if (_T_IS_XTI(api_semantics)) {
82 		/*
83 		 * User level state verification only done for XTI
84 		 * because doing for TLI may break existing applications
85 		 */
86 		if (! (tiptr->ti_state == T_DATAXFER ||
87 			tiptr->ti_state == T_OUTREL)) {
88 			t_errno = TOUTSTATE;
89 			sig_mutex_unlock(&tiptr->ti_lock);
90 			trace3(TR_t_rcv, 1, fd, nbytes);
91 			return (-1);
92 		}
93 	}
94 
95 	/*
96 	 * Check in lookbuf for stuff
97 	 */
98 	if (tiptr->ti_lookcnt > 0) {
99 		/*
100 		 * Implied preference rules give priority to
101 		 * T_DISCON_IND over T_ORDREL_IND. Also certain errors like
102 		 * data received after T_ORDREL_IND or a duplicate T_ORDREL_IND
103 		 * after a T_ORDRELING have priority over TLOOK.
104 		 * This manifests in following code behavior.
105 		 *
106 		 * (1)  If something in lookbuf then check
107 		 *	the stream head also. This may result
108 		 *	in retuning a TLOOK error but only if there are
109 		 *	  - message at stream head but look buffer
110 		 *	    has a T_DISCON_IND event.
111 		 *	  - no messages are on the stream head
112 		 *
113 		 * (2)  If there are messages on the stream head and
114 		 *	all of them are T_ORDREL_IND(i.e. no message in
115 		 *	look buffer is T_DISCON_IND), there
116 		 *	could be data on stream head to be picked up and
117 		 *	we work on the stream head and not return TLOOK.
118 		 *	We remove the event on the stream head and queue it.
119 		 *
120 		 */
121 		do {
122 			retval = _ioctl(fd, I_NREAD, &msglen);
123 		} while (retval < 0 && errno == EINTR);
124 
125 		if (retval < 0) {
126 			sv_errno = errno;
127 
128 			t_errno = TSYSERR;
129 			sig_mutex_unlock(&tiptr->ti_lock);
130 			trace3(TR_t_rcv, 1, fd, nbytes);
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 					trace3(TR_t_rcv, 1, fd, nbytes);
153 					return (-1);
154 				}
155 			} while ((tlbs = tlbs->tl_next) != NULL);
156 
157 		} else {	/* retval == 0 */
158 			/*
159 			 * Nothing on stream head so whatever in
160 			 * look buffer has nothing that might override
161 			 * it.
162 			 */
163 			t_errno = TLOOK;
164 			sig_mutex_unlock(&tiptr->ti_lock);
165 			trace3(TR_t_rcv, 1, fd, nbytes);
166 			return (-1);
167 		}
168 	}
169 
170 	/*
171 	 * Acquire ctlbuf for use in sending/receiving control part
172 	 * of the message.
173 	 */
174 	if (_t_acquire_ctlbuf(tiptr, &ctlbuf, &didalloc) < 0) {
175 		sv_errno = errno;
176 		sig_mutex_unlock(&tiptr->ti_lock);
177 		trace3(TR_t_rcv, 1, fd, nbytes);
178 		errno = sv_errno;
179 		return (-1);
180 	}
181 
182 	databuf.maxlen = nbytes;
183 	databuf.len = 0;
184 	databuf.buf = buf;
185 
186 	*flags = 0;
187 
188 	/*
189 	 * This is a call that may block indefinitely so we drop the
190 	 * lock and allow signals in MT case here and reacquire it.
191 	 * Error case should roll back state changes done above
192 	 * (happens to be no state change here)
193 	 */
194 	sig_mutex_unlock(&tiptr->ti_lock);
195 	if ((retval = getmsg(fd, &ctlbuf, &databuf, &flg)) < 0) {
196 		if (errno == EAGAIN)
197 			t_errno = TNODATA;
198 		else
199 			t_errno = TSYSERR;
200 		sv_errno = errno;
201 		sig_mutex_lock(&tiptr->ti_lock);
202 		errno = sv_errno;
203 		goto err_out;
204 	}
205 	sig_mutex_lock(&tiptr->ti_lock);
206 
207 	assert((retval & MORECTL) == 0); /* MORECTL should not be on */
208 
209 	if (databuf.len == -1) databuf.len = 0;
210 
211 	if (ctlbuf.len > 0) {
212 		if (ctlbuf.len < (int)sizeof (t_scalar_t)) {
213 			t_errno = TSYSERR;
214 			errno = EPROTO;
215 			goto err_out;
216 		}
217 
218 		pptr = (union T_primitives *)ctlbuf.buf;
219 
220 		switch (pptr->type) {
221 
222 		case T_EXDATA_IND:
223 			*flags |= T_EXPEDITED;
224 			if (retval > 0)
225 				tiptr->ti_flags |= EXPEDITED;
226 			/* FALLTHROUGH */
227 		case T_DATA_IND:
228 			/*
229 			 * Uses the fact T_DATA_IND and T_EXDATA_IND
230 			 * are same in size
231 			 */
232 			if ((ctlbuf.len < (int)sizeof (struct T_data_ind)) ||
233 			    (tiptr->ti_lookcnt > 0)) {
234 				/*
235 				 * ti_lookcnt > 0 implies data
236 				 * received after T_DISCON_IND or
237 				 * T_ORDREL_IND hence error
238 				 */
239 				t_errno = TSYSERR;
240 				errno = EPROTO;
241 				goto err_out;
242 			}
243 
244 			if ((pptr->data_ind.MORE_flag) || retval)
245 				*flags |= T_MORE;
246 			if ((pptr->data_ind.MORE_flag) && retval)
247 				tiptr->ti_flags |= MORE;
248 			/*
249 			 * No real state change on T_RCV event (noop)
250 			 *
251 			 * We invoke the macro only for error logging
252 			 * part of its capabilities when in a bad state.
253 			 */
254 			_T_TX_NEXTSTATE(T_RCV, tiptr,
255 					"t_rcv: invalid state event T_RCV");
256 			if (didalloc)
257 				free(ctlbuf.buf);
258 			else
259 				tiptr->ti_ctlbuf = ctlbuf.buf;
260 			sig_mutex_unlock(&tiptr->ti_lock);
261 			trace3(TR_t_rcv, 1, fd, nbytes);
262 			return (databuf.len);
263 
264 		case T_ORDREL_IND:
265 			if (tiptr->ti_lookcnt > 0) {
266 				/*
267 				 * ti_lookcnt > 0 implies T_ORDREL_IND
268 				 * received after T_DISCON_IND or
269 				 * another T_ORDREL_IND hence error.
270 				 */
271 				t_errno = TSYSERR;
272 				errno = EPROTO;
273 				goto err_out;
274 			}
275 			/* FALLTHROUGH */
276 		case T_DISCON_IND:
277 			/*
278 			 * Post event (T_ORDREL_IND/T_DISCON_IND) to
279 			 * the lookbuffer list.
280 			 */
281 
282 			if (_t_register_lookevent(tiptr, databuf.buf,
283 					databuf.len,
284 					ctlbuf.buf, ctlbuf.len) < 0) {
285 				t_errno = TSYSERR;
286 				errno = ENOMEM;
287 				goto err_out;
288 			}
289 			/*
290 			 * We know that T_DISCON_IND is stored in
291 			 * last look buffer. If there is more data
292 			 * that follows, we try to append it to
293 			 * the same look buffer
294 			 */
295 			if (retval & MOREDATA) {
296 				ctlbuf.maxlen = 0; /* XXX why ? */
297 				ctlbuf.len = 0;
298 
299 				/*
300 				 * XXX Will break (-ve maxlen) for
301 				 * transport provider with unbounded
302 				 * T_DISCON_IND data part (-1).
303 				 */
304 				databuf.maxlen =
305 					tiptr->ti_rcvsize - databuf.len;
306 
307 				databuf.len = 0;
308 				databuf.buf =
309 					tiptr->ti_lookbufs.tl_lookdbuf +
310 					tiptr->ti_lookbufs.tl_lookdlen;
311 				*flags = 0;
312 
313 				/*
314 				 * Since MOREDATA was set, we assume
315 				 * that this getmsg will not block
316 				 * indefinitely
317 				 */
318 				do {
319 					retval = getmsg(fd, &ctlbuf,
320 							&databuf, &flg);
321 				} while (retval < 0 && errno == EINTR);
322 
323 				if (retval < 0) {
324 					t_errno = TSYSERR;
325 					goto err_out;
326 				}
327 				if (databuf.len == -1) databuf.len = 0;
328 				if (retval > 0) {
329 					/* MORECTL should not be on */
330 					assert((retval & MORECTL) == 0);
331 					/*
332 					 * XXX - Why ?
333 					 * No support for unbounded data
334 					 * on T_DISCON_IND ?
335 					 */
336 					t_errno = TSYSERR;
337 					errno = EPROTO;
338 					goto err_out;
339 				}
340 				tiptr->ti_lookbufs.tl_lookdlen +=
341 					databuf.len;
342 			}
343 
344 			t_errno = TLOOK;
345 			goto err_out;
346 
347 		default:
348 			break;
349 		}
350 
351 		t_errno = TSYSERR;
352 		errno = EPROTO;
353 		goto err_out;
354 
355 	} else {		/* else for "if (ctlbuf.len > 0)" */
356 		if (!retval && (tiptr->ti_flags & MORE)) {
357 			*flags |= T_MORE;
358 			tiptr->ti_flags &= ~MORE;
359 		}
360 		if (retval & MOREDATA)
361 			*flags |= T_MORE;
362 
363 		/*
364 		 * If inside an ETSDU, set expedited flag and turn
365 		 * of internal version when reach end of "ETIDU".
366 		 */
367 		if (tiptr->ti_flags & EXPEDITED) {
368 			*flags |= T_EXPEDITED;
369 			if (!retval)
370 				tiptr->ti_flags &= ~EXPEDITED;
371 		}
372 
373 		/*
374 		 * No real state change on T_RCV events (It is a NOOP)
375 		 *
376 		 * We invoke the macro only for error logging
377 		 * part of its capabilities when in a bad state.
378 		 */
379 		_T_TX_NEXTSTATE(T_RCV, tiptr,
380 			"t_rcv: state invalid T_RCV event");
381 		if (didalloc)
382 			free(ctlbuf.buf);
383 		else
384 			tiptr->ti_ctlbuf = ctlbuf.buf;
385 		sig_mutex_unlock(&tiptr->ti_lock);
386 		trace3(TR_t_rcv, 1, fd, nbytes);
387 		return (databuf.len);
388 	}
389 	/* NOTREACHED */
390 
391 err_out:
392 	sv_errno = errno;
393 	if (didalloc)
394 		free(ctlbuf.buf);
395 	else
396 		tiptr->ti_ctlbuf = ctlbuf.buf;
397 	sig_mutex_unlock(&tiptr->ti_lock);
398 
399 	trace3(TR_t_rcv, 1, fd, nbytes);
400 	errno = sv_errno;
401 	return (-1);
402 }
403