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