[http] Cleanly shut down potentially looped interfaces
[ipxe.git] / src / net / tcp / oncrpc.c
1 /*
2 * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <assert.h>
25 #include <errno.h>
26 #include <byteswap.h>
27 #include <ipxe/socket.h>
28 #include <ipxe/tcpip.h>
29 #include <ipxe/in.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/dhcp.h>
32 #include <ipxe/xfer.h>
33 #include <ipxe/open.h>
34 #include <ipxe/uri.h>
35 #include <ipxe/features.h>
36 #include <ipxe/oncrpc.h>
37 #include <ipxe/oncrpc_iob.h>
38 #include <ipxe/init.h>
39 #include <ipxe/settings.h>
40 #include <ipxe/version.h>
41
42 /** @file
43 *
44 * SUN ONC RPC protocol
45 *
46 */
47
48 /** Set most significant bit to 1. */
49 #define SET_LAST_FRAME( x ) ( (x) | 1 << 31 )
50 #define GET_FRAME_SIZE( x ) ( (x) & ~( 1 << 31 ) )
51
52 #define ONCRPC_CALL 0
53 #define ONCRPC_REPLY 1
54
55 /** AUTH NONE authentication flavor */
56 struct oncrpc_cred oncrpc_auth_none = {
57 .flavor = ONCRPC_AUTH_NONE,
58 .length = 0
59 };
60
61 const struct setting uid_setting __setting ( SETTING_AUTH, uid ) = {
62 .name = "uid",
63 .description = "User ID",
64 .tag = DHCP_EB_UID,
65 .type = &setting_type_uint32
66 };
67
68 const struct setting gid_setting __setting ( SETTING_AUTH, gid ) = {
69 .name = "gid",
70 .description = "Group ID",
71 .tag = DHCP_EB_GID,
72 .type = &setting_type_uint32
73 };
74
75 /**
76 * Initialize an ONC RPC AUTH SYS credential structure
77 *
78 * @v auth_sys The structure to initialize
79 *
80 * The hostname field is filled with the value of the hostname setting, if the
81 * hostname setting is empty, PRODUCT_SHORT_NAME (usually "iPXE") is used
82 * instead.
83 */
84 int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ) {
85 if ( ! auth_sys )
86 return -EINVAL;
87
88 fetch_string_setting_copy ( NULL, &hostname_setting,
89 &auth_sys->hostname );
90 if ( ! auth_sys->hostname )
91 if ( ! ( auth_sys->hostname = strdup ( product_short_name ) ) )
92 return -ENOMEM;
93
94 auth_sys->uid = fetch_uintz_setting ( NULL, &uid_setting );
95 auth_sys->gid = fetch_uintz_setting ( NULL, &uid_setting );
96 auth_sys->aux_gid_len = 0;
97 auth_sys->stamp = 0;
98
99 auth_sys->credential.flavor = ONCRPC_AUTH_SYS;
100 auth_sys->credential.length = 16 +
101 oncrpc_strlen ( auth_sys->hostname );
102
103 return 0;
104 }
105
106 /**
107 * Prepare an ONC RPC session structure to be used by the ONC RPC layer
108 *
109 * @v session ONC RPC session
110 * @v credential Credential structure pointer
111 * @v verifier Verifier structure pointer
112 * @v prog_name ONC RPC program number
113 * @v prog_vers ONC RPC program version number
114 */
115 void oncrpc_init_session ( struct oncrpc_session *session,
116 struct oncrpc_cred *credential,
117 struct oncrpc_cred *verifier, uint32_t prog_name,
118 uint32_t prog_vers ) {
119 if ( ! session )
120 return;
121
122 session->rpc_id = rand();
123 session->credential = credential;
124 session->verifier = verifier;
125 session->prog_name = prog_name;
126 session->prog_vers = prog_vers;
127 }
128
129 int oncrpc_call ( struct interface *intf, struct oncrpc_session *session,
130 uint32_t proc_name, const struct oncrpc_field fields[] ) {
131 int rc;
132 size_t frame_size;
133 struct io_buffer *io_buf;
134
135 if ( ! session )
136 return -EINVAL;
137
138 struct oncrpc_field header[] = {
139 ONCRPC_FIELD ( int32, 0 ),
140 ONCRPC_FIELD ( int32, ++session->rpc_id ),
141 ONCRPC_FIELD ( int32, ONCRPC_CALL ),
142 ONCRPC_FIELD ( int32, ONCRPC_VERS ),
143 ONCRPC_FIELD ( int32, session->prog_name ),
144 ONCRPC_FIELD ( int32, session->prog_vers ),
145 ONCRPC_FIELD ( int32, proc_name ),
146 ONCRPC_FIELD ( cred, session->credential ),
147 ONCRPC_FIELD ( cred, session->verifier ),
148 ONCRPC_FIELD_END,
149 };
150
151 frame_size = oncrpc_compute_size ( header );
152 frame_size += oncrpc_compute_size ( fields );
153
154 io_buf = alloc_iob ( frame_size );
155 if ( ! io_buf )
156 return -ENOBUFS;
157
158 header[0].value.int32 = SET_LAST_FRAME ( frame_size -
159 sizeof ( uint32_t ) );
160
161 oncrpc_iob_add_fields ( io_buf, header );
162 oncrpc_iob_add_fields ( io_buf, fields );
163
164 rc = xfer_deliver_iob ( intf, io_buf );
165 if ( rc != 0 )
166 free_iob ( io_buf );
167
168 return rc;
169 }
170
171 size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) {
172
173 size_t i;
174 size_t size = 0;
175
176 for ( i = 0; fields[i].type != oncrpc_none; i++ ) {
177 switch ( fields[i].type ) {
178 case oncrpc_int32:
179 size += sizeof ( uint32_t );
180 break;
181
182 case oncrpc_int64:
183 size += sizeof ( uint64_t );
184 break;
185
186 case oncrpc_str:
187 size += oncrpc_strlen ( fields[i].value.str );
188 break;
189
190 case oncrpc_array:
191 size += oncrpc_align ( fields[i].value.array.length );
192 size += sizeof ( uint32_t );
193 break;
194
195 case oncrpc_intarray:
196 size += sizeof ( uint32_t ) *
197 fields[i].value.intarray.length;
198 size += sizeof ( uint32_t );
199 break;
200
201 case oncrpc_cred:
202 size += fields[i].value.cred->length;
203 size += 2 * sizeof ( uint32_t );
204 break;
205
206 default:
207 return size;
208 }
209 }
210
211 return size;
212 }
213
214 /**
215 * Parse an I/O buffer to extract a ONC RPC REPLY
216 * @v session ONC RPC session
217 * @v reply Reply structure where data will be saved
218 * @v io_buf I/O buffer
219 */
220 int oncrpc_get_reply ( struct oncrpc_session *session __unused,
221 struct oncrpc_reply *reply, struct io_buffer *io_buf ) {
222 if ( ! reply || ! io_buf )
223 return -EINVAL;
224
225 reply->frame_size = GET_FRAME_SIZE ( oncrpc_iob_get_int ( io_buf ) );
226 reply->rpc_id = oncrpc_iob_get_int ( io_buf );
227
228 /* iPXE has no support for handling ONC RPC call */
229 if ( oncrpc_iob_get_int ( io_buf ) != ONCRPC_REPLY )
230 return -ENOSYS;
231
232 reply->reply_state = oncrpc_iob_get_int ( io_buf );
233
234 if ( reply->reply_state == 0 )
235 {
236 /* verifier.flavor */
237 oncrpc_iob_get_int ( io_buf );
238 /* verifier.length */
239 iob_pull ( io_buf, oncrpc_iob_get_int ( io_buf ));
240
241 /* We don't use the verifier in iPXE, let it be an empty
242 verifier. */
243 reply->verifier = &oncrpc_auth_none;
244 }
245
246 reply->accept_state = oncrpc_iob_get_int ( io_buf );
247 reply->data = io_buf;
248
249 return 0;
250 }