s3: Add an async smbsock_connect
[obnox/samba-ctdb.git] / source3 / libsmb / smbsock_connect.c
1 /*
2    Unix SMB/CIFS implementation.
3    Connect to 445 and 139/nbsesssetup
4    Copyright (C) Volker Lendecke 2010
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "../lib/async_req/async_sock.h"
22 #include "async_smb.h"
23
24 struct nb_connect_state {
25         struct tevent_context *ev;
26         int sock;
27         struct nmb_name called;
28         struct nmb_name calling;
29 };
30
31 static int nb_connect_state_destructor(struct nb_connect_state *state);
32 static void nb_connect_connected(struct tevent_req *subreq);
33 static void nb_connect_done(struct tevent_req *subreq);
34
35 static struct tevent_req *nb_connect_send(TALLOC_CTX *mem_ctx,
36                                           struct tevent_context *ev,
37                                           const struct sockaddr_storage *addr,
38                                           const char *called_name,
39                                           int called_type,
40                                           const char *calling_name,
41                                           int calling_type)
42 {
43         struct tevent_req *req, *subreq;
44         struct nb_connect_state *state;
45
46         req = tevent_req_create(mem_ctx, &state, struct nb_connect_state);
47         if (req == NULL) {
48                 return NULL;
49         }
50         state->ev = ev;
51         make_nmb_name(&state->called, called_name, called_type);
52         make_nmb_name(&state->calling, calling_name, calling_type);
53         state->sock = -1;
54
55         talloc_set_destructor(state, nb_connect_state_destructor);
56
57         subreq = open_socket_out_send(state, ev, addr, 139, 5000);
58         if (tevent_req_nomem(subreq, req)) {
59                 return tevent_req_post(req, ev);
60         }
61         tevent_req_set_callback(subreq, nb_connect_connected, req);
62         return req;
63 }
64
65 static int nb_connect_state_destructor(struct nb_connect_state *state)
66 {
67         if (state->sock != -1) {
68                 close(state->sock);
69         }
70         return 0;
71 }
72
73 static void nb_connect_connected(struct tevent_req *subreq)
74 {
75         struct tevent_req *req = tevent_req_callback_data(
76                 subreq, struct tevent_req);
77         struct nb_connect_state *state = tevent_req_data(
78                 req, struct nb_connect_state);
79         NTSTATUS status;
80
81         status = open_socket_out_recv(subreq, &state->sock);
82         TALLOC_FREE(subreq);
83         if (!NT_STATUS_IS_OK(status)) {
84                 tevent_req_nterror(req, status);
85                 return;
86         }
87         subreq = cli_session_request_send(state, state->ev, state->sock,
88                                           &state->called, &state->calling);
89         if (tevent_req_nomem(subreq, req)) {
90                 return;
91         }
92         tevent_req_set_callback(subreq, nb_connect_done, req);
93 }
94
95 static void nb_connect_done(struct tevent_req *subreq)
96 {
97         struct tevent_req *req = tevent_req_callback_data(
98                 subreq, struct tevent_req);
99         bool ret;
100         int err;
101         uint8_t resp;
102
103         ret = cli_session_request_recv(subreq, &err, &resp);
104         TALLOC_FREE(subreq);
105         if (!ret) {
106                 tevent_req_nterror(req, map_nt_error_from_unix(err));
107                 return;
108         }
109         if (resp != 0x82) {
110                 tevent_req_nterror(req, NT_STATUS_RESOURCE_NAME_NOT_FOUND);
111                 return;
112         }
113         tevent_req_done(req);
114 }
115
116 static NTSTATUS nb_connect_recv(struct tevent_req *req, int *sock)
117 {
118         struct nb_connect_state *state = tevent_req_data(
119                 req, struct nb_connect_state);
120         NTSTATUS status;
121
122         if (tevent_req_is_nterror(req, &status)) {
123                 return status;
124         }
125         *sock = state->sock;
126         state->sock = -1;
127         return NT_STATUS_OK;
128 }
129
130 struct smbsock_connect_state {
131         struct tevent_context *ev;
132         const struct sockaddr_storage *addr;
133         const char *called_name;
134         const char *calling_name;
135         struct tevent_req *req_139;
136         struct tevent_req *req_445;
137         int sock;
138         uint16_t port;
139 };
140
141 static int smbsock_connect_state_destructor(
142         struct smbsock_connect_state *state);
143 static void smbsock_connect_connected(struct tevent_req *subreq);
144 static void smbsock_connect_do_139(struct tevent_req *subreq);
145
146 struct tevent_req *smbsock_connect_send(TALLOC_CTX *mem_ctx,
147                                         struct tevent_context *ev,
148                                         const struct sockaddr_storage *addr,
149                                         const char *called_name,
150                                         const char *calling_name)
151 {
152         struct tevent_req *req, *subreq;
153         struct smbsock_connect_state *state;
154
155         req = tevent_req_create(mem_ctx, &state, struct smbsock_connect_state);
156         if (req == NULL) {
157                 return NULL;
158         }
159         state->ev = ev;
160         state->addr = addr;
161         state->sock = -1;
162         state->called_name =
163                 (called_name != NULL) ? called_name : "*SMBSERVER";
164         state->calling_name =
165                 (calling_name != NULL) ? calling_name : global_myname();
166
167         talloc_set_destructor(state, smbsock_connect_state_destructor);
168
169         state->req_445 = open_socket_out_send(state, ev, addr, 445, 5000);
170         if (tevent_req_nomem(state->req_445, req)) {
171                 return tevent_req_post(req, ev);
172         }
173         tevent_req_set_callback(state->req_445, smbsock_connect_connected,
174                                 req);
175
176         /*
177          * After 5 msecs, fire the 139 request
178          */
179         subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(0, 5000));
180         if (tevent_req_nomem(subreq, req)) {
181                 TALLOC_FREE(state->req_445);
182                 return tevent_req_post(req, ev);
183         }
184         tevent_req_set_callback(subreq, smbsock_connect_do_139, req);
185         return req;
186 }
187
188 static int smbsock_connect_state_destructor(
189         struct smbsock_connect_state *state)
190 {
191         if (state->sock != -1) {
192                 close(state->sock);
193         }
194         return 0;
195 }
196
197 static void smbsock_connect_do_139(struct tevent_req *subreq)
198 {
199         struct tevent_req *req = tevent_req_callback_data(
200                 subreq, struct tevent_req);
201         struct smbsock_connect_state *state = tevent_req_data(
202                 req, struct smbsock_connect_state);
203         bool ret;
204
205         ret = tevent_wakeup_recv(subreq);
206         TALLOC_FREE(subreq);
207         if (!ret) {
208                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
209                 return;
210         }
211         state->req_139 = nb_connect_send(state, state->ev, state->addr,
212                                          state->called_name, 0x20,
213                                          state->calling_name, 0x0);
214         if (tevent_req_nomem(state->req_139, req)) {
215                 return;
216         }
217         tevent_req_set_callback(state->req_139, smbsock_connect_connected,
218                                 req);
219 }
220
221 static void smbsock_connect_connected(struct tevent_req *subreq)
222 {
223         struct tevent_req *req = tevent_req_callback_data(
224                 subreq, struct tevent_req);
225         struct smbsock_connect_state *state = tevent_req_data(
226                 req, struct smbsock_connect_state);
227         struct tevent_req *unfinished_req;
228         NTSTATUS status;
229
230         if (subreq == state->req_445) {
231
232                 status = open_socket_out_recv(subreq, &state->sock);
233                 TALLOC_FREE(state->req_445);
234                 unfinished_req = state->req_139;
235                 state->port = 445;
236
237         } else if (subreq == state->req_139) {
238
239                 status = nb_connect_recv(subreq, &state->sock);
240                 TALLOC_FREE(state->req_139);
241                 unfinished_req = state->req_445;
242                 state->port = 139;
243
244         } else {
245                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
246                 return;
247         }
248
249         if (NT_STATUS_IS_OK(status)) {
250                 TALLOC_FREE(unfinished_req);
251                 state->req_139 = NULL;
252                 state->req_445 = NULL;
253                 tevent_req_done(req);
254                 return;
255         }
256         if (unfinished_req == NULL) {
257                 /*
258                  * Both requests failed
259                  */
260                 tevent_req_nterror(req, status);
261                 return;
262         }
263         /*
264          * Do nothing, wait for the second request to come here.
265          */
266 }
267
268 NTSTATUS smbsock_connect_recv(struct tevent_req *req, int *sock,
269                           uint16_t *port)
270 {
271         struct smbsock_connect_state *state = tevent_req_data(
272                 req, struct smbsock_connect_state);
273         NTSTATUS status;
274
275         if (tevent_req_is_nterror(req, &status)) {
276                 return status;
277         }
278         *sock = state->sock;
279         state->sock = -1;
280         *port = state->port;
281         return NT_STATUS_OK;
282 }
283
284 NTSTATUS smbsock_connect(const struct sockaddr_storage *addr,
285                          const char *called_name, const char *calling_name,
286                          int *pfd, uint16_t *port)
287 {
288         TALLOC_CTX *frame = talloc_stackframe();
289         struct event_context *ev;
290         struct tevent_req *req;
291         NTSTATUS status = NT_STATUS_NO_MEMORY;
292
293         ev = event_context_init(frame);
294         if (ev == NULL) {
295                 goto fail;
296         }
297         req = smbsock_connect_send(frame, ev, addr, called_name, calling_name);
298         if (req == NULL) {
299                 goto fail;
300         }
301         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
302                 goto fail;
303         }
304         status = smbsock_connect_recv(req, pfd, port);
305  fail:
306         TALLOC_FREE(frame);
307         return status;
308 }