Line | Count | Source (jump to first uncovered line) |
1 | | #include "git-compat-util.h" |
2 | | #include "config.h" |
3 | | #include "environment.h" |
4 | | #include "gettext.h" |
5 | | #include "hex.h" |
6 | | #include "pkt-line.h" |
7 | | #include "quote.h" |
8 | | #include "refs.h" |
9 | | #include "run-command.h" |
10 | | #include "remote.h" |
11 | | #include "connect.h" |
12 | | #include "url.h" |
13 | | #include "string-list.h" |
14 | | #include "oid-array.h" |
15 | | #include "path.h" |
16 | | #include "transport.h" |
17 | | #include "trace2.h" |
18 | | #include "strbuf.h" |
19 | | #include "version.h" |
20 | | #include "protocol.h" |
21 | | #include "alias.h" |
22 | | #include "bundle-uri.h" |
23 | | |
24 | | static char *server_capabilities_v1; |
25 | | static struct strvec server_capabilities_v2 = STRVEC_INIT; |
26 | | static const char *next_server_feature_value(const char *feature, size_t *len, size_t *offset); |
27 | | |
28 | | static int check_ref(const char *name, unsigned int flags) |
29 | 0 | { |
30 | 0 | if (!flags) |
31 | 0 | return 1; |
32 | | |
33 | 0 | if (!skip_prefix(name, "refs/", &name)) |
34 | 0 | return 0; |
35 | | |
36 | | /* REF_NORMAL means that we don't want the magic fake tag refs */ |
37 | 0 | if ((flags & REF_NORMAL) && check_refname_format(name, |
38 | 0 | REFNAME_ALLOW_ONELEVEL)) |
39 | 0 | return 0; |
40 | | |
41 | | /* REF_HEADS means that we want regular branch heads */ |
42 | 0 | if ((flags & REF_HEADS) && starts_with(name, "heads/")) |
43 | 0 | return 1; |
44 | | |
45 | | /* REF_TAGS means that we want tags */ |
46 | 0 | if ((flags & REF_TAGS) && starts_with(name, "tags/")) |
47 | 0 | return 1; |
48 | | |
49 | | /* All type bits clear means that we are ok with anything */ |
50 | 0 | return !(flags & ~REF_NORMAL); |
51 | 0 | } |
52 | | |
53 | | int check_ref_type(const struct ref *ref, int flags) |
54 | 0 | { |
55 | 0 | return check_ref(ref->name, flags); |
56 | 0 | } |
57 | | |
58 | | static NORETURN void die_initial_contact(int unexpected) |
59 | 0 | { |
60 | | /* |
61 | | * A hang-up after seeing some response from the other end |
62 | | * means that it is unexpected, as we know the other end is |
63 | | * willing to talk to us. A hang-up before seeing any |
64 | | * response does not necessarily mean an ACL problem, though. |
65 | | */ |
66 | 0 | if (unexpected) |
67 | 0 | die(_("the remote end hung up upon initial contact")); |
68 | 0 | else |
69 | 0 | die(_("Could not read from remote repository.\n\n" |
70 | 0 | "Please make sure you have the correct access rights\n" |
71 | 0 | "and the repository exists.")); |
72 | 0 | } |
73 | | |
74 | | /* Checks if the server supports the capability 'c' */ |
75 | | int server_supports_v2(const char *c) |
76 | 0 | { |
77 | 0 | int i; |
78 | |
|
79 | 0 | for (i = 0; i < server_capabilities_v2.nr; i++) { |
80 | 0 | const char *out; |
81 | 0 | if (skip_prefix(server_capabilities_v2.v[i], c, &out) && |
82 | 0 | (!*out || *out == '=')) |
83 | 0 | return 1; |
84 | 0 | } |
85 | 0 | return 0; |
86 | 0 | } |
87 | | |
88 | | void ensure_server_supports_v2(const char *c) |
89 | 0 | { |
90 | 0 | if (!server_supports_v2(c)) |
91 | 0 | die(_("server doesn't support '%s'"), c); |
92 | 0 | } |
93 | | |
94 | | int server_feature_v2(const char *c, const char **v) |
95 | 0 | { |
96 | 0 | int i; |
97 | |
|
98 | 0 | for (i = 0; i < server_capabilities_v2.nr; i++) { |
99 | 0 | const char *out; |
100 | 0 | if (skip_prefix(server_capabilities_v2.v[i], c, &out) && |
101 | 0 | (*out == '=')) { |
102 | 0 | *v = out + 1; |
103 | 0 | return 1; |
104 | 0 | } |
105 | 0 | } |
106 | 0 | return 0; |
107 | 0 | } |
108 | | |
109 | | int server_supports_feature(const char *c, const char *feature, |
110 | | int die_on_error) |
111 | 0 | { |
112 | 0 | int i; |
113 | |
|
114 | 0 | for (i = 0; i < server_capabilities_v2.nr; i++) { |
115 | 0 | const char *out; |
116 | 0 | if (skip_prefix(server_capabilities_v2.v[i], c, &out) && |
117 | 0 | (!*out || *(out++) == '=')) { |
118 | 0 | if (parse_feature_request(out, feature)) |
119 | 0 | return 1; |
120 | 0 | else |
121 | 0 | break; |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | 0 | if (die_on_error) |
126 | 0 | die(_("server doesn't support feature '%s'"), feature); |
127 | | |
128 | 0 | return 0; |
129 | 0 | } |
130 | | |
131 | | static void process_capabilities_v2(struct packet_reader *reader) |
132 | 0 | { |
133 | 0 | while (packet_reader_read(reader) == PACKET_READ_NORMAL) |
134 | 0 | strvec_push(&server_capabilities_v2, reader->line); |
135 | |
|
136 | 0 | if (reader->status != PACKET_READ_FLUSH) |
137 | 0 | die(_("expected flush after capabilities")); |
138 | 0 | } |
139 | | |
140 | | enum protocol_version discover_version(struct packet_reader *reader) |
141 | 0 | { |
142 | 0 | enum protocol_version version = protocol_unknown_version; |
143 | | |
144 | | /* |
145 | | * Peek the first line of the server's response to |
146 | | * determine the protocol version the server is speaking. |
147 | | */ |
148 | 0 | switch (packet_reader_peek(reader)) { |
149 | 0 | case PACKET_READ_EOF: |
150 | 0 | die_initial_contact(0); |
151 | 0 | case PACKET_READ_FLUSH: |
152 | 0 | case PACKET_READ_DELIM: |
153 | 0 | case PACKET_READ_RESPONSE_END: |
154 | 0 | version = protocol_v0; |
155 | 0 | break; |
156 | 0 | case PACKET_READ_NORMAL: |
157 | 0 | version = determine_protocol_version_client(reader->line); |
158 | 0 | break; |
159 | 0 | } |
160 | | |
161 | 0 | switch (version) { |
162 | 0 | case protocol_v2: |
163 | 0 | process_capabilities_v2(reader); |
164 | 0 | break; |
165 | 0 | case protocol_v1: |
166 | | /* Read the peeked version line */ |
167 | 0 | packet_reader_read(reader); |
168 | 0 | break; |
169 | 0 | case protocol_v0: |
170 | 0 | break; |
171 | 0 | case protocol_unknown_version: |
172 | 0 | BUG("unknown protocol version"); |
173 | 0 | } |
174 | | |
175 | 0 | trace2_data_intmax("transfer", NULL, "negotiated-version", version); |
176 | |
|
177 | 0 | return version; |
178 | 0 | } |
179 | | |
180 | | static void parse_one_symref_info(struct string_list *symref, const char *val, int len) |
181 | 0 | { |
182 | 0 | char *sym, *target; |
183 | 0 | struct string_list_item *item; |
184 | |
|
185 | 0 | if (!len) |
186 | 0 | return; /* just "symref" */ |
187 | | /* e.g. "symref=HEAD:refs/heads/master" */ |
188 | 0 | sym = xmemdupz(val, len); |
189 | 0 | target = strchr(sym, ':'); |
190 | 0 | if (!target) |
191 | | /* just "symref=something" */ |
192 | 0 | goto reject; |
193 | 0 | *(target++) = '\0'; |
194 | 0 | if (check_refname_format(sym, REFNAME_ALLOW_ONELEVEL) || |
195 | 0 | check_refname_format(target, REFNAME_ALLOW_ONELEVEL)) |
196 | | /* "symref=bogus:pair */ |
197 | 0 | goto reject; |
198 | 0 | item = string_list_append_nodup(symref, sym); |
199 | 0 | item->util = target; |
200 | 0 | return; |
201 | 0 | reject: |
202 | 0 | free(sym); |
203 | 0 | return; |
204 | 0 | } |
205 | | |
206 | | static void annotate_refs_with_symref_info(struct ref *ref) |
207 | 0 | { |
208 | 0 | struct string_list symref = STRING_LIST_INIT_DUP; |
209 | 0 | size_t offset = 0; |
210 | |
|
211 | 0 | while (1) { |
212 | 0 | size_t len; |
213 | 0 | const char *val; |
214 | |
|
215 | 0 | val = next_server_feature_value("symref", &len, &offset); |
216 | 0 | if (!val) |
217 | 0 | break; |
218 | 0 | parse_one_symref_info(&symref, val, len); |
219 | 0 | } |
220 | 0 | string_list_sort(&symref); |
221 | |
|
222 | 0 | for (; ref; ref = ref->next) { |
223 | 0 | struct string_list_item *item; |
224 | 0 | item = string_list_lookup(&symref, ref->name); |
225 | 0 | if (!item) |
226 | 0 | continue; |
227 | 0 | ref->symref = xstrdup((char *)item->util); |
228 | 0 | } |
229 | 0 | string_list_clear(&symref, 0); |
230 | 0 | } |
231 | | |
232 | | static void process_capabilities(struct packet_reader *reader, int *linelen) |
233 | 0 | { |
234 | 0 | const char *feat_val; |
235 | 0 | size_t feat_len; |
236 | 0 | const char *line = reader->line; |
237 | 0 | int nul_location = strlen(line); |
238 | 0 | if (nul_location == *linelen) |
239 | 0 | return; |
240 | 0 | server_capabilities_v1 = xstrdup(line + nul_location + 1); |
241 | 0 | *linelen = nul_location; |
242 | |
|
243 | 0 | feat_val = server_feature_value("object-format", &feat_len); |
244 | 0 | if (feat_val) { |
245 | 0 | char *hash_name = xstrndup(feat_val, feat_len); |
246 | 0 | int hash_algo = hash_algo_by_name(hash_name); |
247 | 0 | if (hash_algo != GIT_HASH_UNKNOWN) |
248 | 0 | reader->hash_algo = &hash_algos[hash_algo]; |
249 | 0 | free(hash_name); |
250 | 0 | } else { |
251 | 0 | reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | static int process_dummy_ref(const struct packet_reader *reader) |
256 | 0 | { |
257 | 0 | const char *line = reader->line; |
258 | 0 | struct object_id oid; |
259 | 0 | const char *name; |
260 | |
|
261 | 0 | if (parse_oid_hex_algop(line, &oid, &name, reader->hash_algo)) |
262 | 0 | return 0; |
263 | 0 | if (*name != ' ') |
264 | 0 | return 0; |
265 | 0 | name++; |
266 | |
|
267 | 0 | return oideq(reader->hash_algo->null_oid, &oid) && |
268 | 0 | !strcmp(name, "capabilities^{}"); |
269 | 0 | } |
270 | | |
271 | | static void check_no_capabilities(const char *line, int len) |
272 | 0 | { |
273 | 0 | if (strlen(line) != len) |
274 | 0 | warning(_("ignoring capabilities after first line '%s'"), |
275 | 0 | line + strlen(line)); |
276 | 0 | } |
277 | | |
278 | | static int process_ref(const struct packet_reader *reader, int len, |
279 | | struct ref ***list, unsigned int flags, |
280 | | struct oid_array *extra_have) |
281 | 0 | { |
282 | 0 | const char *line = reader->line; |
283 | 0 | struct object_id old_oid; |
284 | 0 | const char *name; |
285 | |
|
286 | 0 | if (parse_oid_hex_algop(line, &old_oid, &name, reader->hash_algo)) |
287 | 0 | return 0; |
288 | 0 | if (*name != ' ') |
289 | 0 | return 0; |
290 | 0 | name++; |
291 | |
|
292 | 0 | if (extra_have && !strcmp(name, ".have")) { |
293 | 0 | oid_array_append(extra_have, &old_oid); |
294 | 0 | } else if (!strcmp(name, "capabilities^{}")) { |
295 | 0 | die(_("protocol error: unexpected capabilities^{}")); |
296 | 0 | } else if (check_ref(name, flags)) { |
297 | 0 | struct ref *ref = alloc_ref(name); |
298 | 0 | oidcpy(&ref->old_oid, &old_oid); |
299 | 0 | **list = ref; |
300 | 0 | *list = &ref->next; |
301 | 0 | } |
302 | 0 | check_no_capabilities(line, len); |
303 | 0 | return 1; |
304 | 0 | } |
305 | | |
306 | | static int process_shallow(const struct packet_reader *reader, int len, |
307 | | struct oid_array *shallow_points) |
308 | 0 | { |
309 | 0 | const char *line = reader->line; |
310 | 0 | const char *arg; |
311 | 0 | struct object_id old_oid; |
312 | |
|
313 | 0 | if (!skip_prefix(line, "shallow ", &arg)) |
314 | 0 | return 0; |
315 | | |
316 | 0 | if (get_oid_hex_algop(arg, &old_oid, reader->hash_algo)) |
317 | 0 | die(_("protocol error: expected shallow sha-1, got '%s'"), arg); |
318 | 0 | if (!shallow_points) |
319 | 0 | die(_("repository on the other end cannot be shallow")); |
320 | 0 | oid_array_append(shallow_points, &old_oid); |
321 | 0 | check_no_capabilities(line, len); |
322 | 0 | return 1; |
323 | 0 | } |
324 | | |
325 | | enum get_remote_heads_state { |
326 | | EXPECTING_FIRST_REF = 0, |
327 | | EXPECTING_REF, |
328 | | EXPECTING_SHALLOW, |
329 | | EXPECTING_DONE, |
330 | | }; |
331 | | |
332 | | /* |
333 | | * Read all the refs from the other end |
334 | | */ |
335 | | struct ref **get_remote_heads(struct packet_reader *reader, |
336 | | struct ref **list, unsigned int flags, |
337 | | struct oid_array *extra_have, |
338 | | struct oid_array *shallow_points) |
339 | 0 | { |
340 | 0 | struct ref **orig_list = list; |
341 | 0 | int len = 0; |
342 | 0 | enum get_remote_heads_state state = EXPECTING_FIRST_REF; |
343 | |
|
344 | 0 | *list = NULL; |
345 | |
|
346 | 0 | while (state != EXPECTING_DONE) { |
347 | 0 | switch (packet_reader_read(reader)) { |
348 | 0 | case PACKET_READ_EOF: |
349 | 0 | die_initial_contact(1); |
350 | 0 | case PACKET_READ_NORMAL: |
351 | 0 | len = reader->pktlen; |
352 | 0 | break; |
353 | 0 | case PACKET_READ_FLUSH: |
354 | 0 | state = EXPECTING_DONE; |
355 | 0 | break; |
356 | 0 | case PACKET_READ_DELIM: |
357 | 0 | case PACKET_READ_RESPONSE_END: |
358 | 0 | die(_("invalid packet")); |
359 | 0 | } |
360 | | |
361 | 0 | switch (state) { |
362 | 0 | case EXPECTING_FIRST_REF: |
363 | 0 | process_capabilities(reader, &len); |
364 | 0 | if (process_dummy_ref(reader)) { |
365 | 0 | state = EXPECTING_SHALLOW; |
366 | 0 | break; |
367 | 0 | } |
368 | 0 | state = EXPECTING_REF; |
369 | | /* fallthrough */ |
370 | 0 | case EXPECTING_REF: |
371 | 0 | if (process_ref(reader, len, &list, flags, extra_have)) |
372 | 0 | break; |
373 | 0 | state = EXPECTING_SHALLOW; |
374 | | /* fallthrough */ |
375 | 0 | case EXPECTING_SHALLOW: |
376 | 0 | if (process_shallow(reader, len, shallow_points)) |
377 | 0 | break; |
378 | 0 | die(_("protocol error: unexpected '%s'"), reader->line); |
379 | 0 | case EXPECTING_DONE: |
380 | 0 | break; |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | 0 | annotate_refs_with_symref_info(*orig_list); |
385 | |
|
386 | 0 | return list; |
387 | 0 | } |
388 | | |
389 | | /* Returns 1 when a valid ref has been added to `list`, 0 otherwise */ |
390 | | static int process_ref_v2(struct packet_reader *reader, struct ref ***list, |
391 | | const char **unborn_head_target) |
392 | 0 | { |
393 | 0 | int ret = 1; |
394 | 0 | int i = 0; |
395 | 0 | struct object_id old_oid; |
396 | 0 | struct ref *ref; |
397 | 0 | struct string_list line_sections = STRING_LIST_INIT_DUP; |
398 | 0 | const char *end; |
399 | 0 | const char *line = reader->line; |
400 | | |
401 | | /* |
402 | | * Ref lines have a number of fields which are space deliminated. The |
403 | | * first field is the OID of the ref. The second field is the ref |
404 | | * name. Subsequent fields (symref-target and peeled) are optional and |
405 | | * don't have a particular order. |
406 | | */ |
407 | 0 | if (string_list_split(&line_sections, line, ' ', -1) < 2) { |
408 | 0 | ret = 0; |
409 | 0 | goto out; |
410 | 0 | } |
411 | | |
412 | 0 | if (!strcmp("unborn", line_sections.items[i].string)) { |
413 | 0 | i++; |
414 | 0 | if (unborn_head_target && |
415 | 0 | !strcmp("HEAD", line_sections.items[i++].string)) { |
416 | | /* |
417 | | * Look for the symref target (if any). If found, |
418 | | * return it to the caller. |
419 | | */ |
420 | 0 | for (; i < line_sections.nr; i++) { |
421 | 0 | const char *arg = line_sections.items[i].string; |
422 | |
|
423 | 0 | if (skip_prefix(arg, "symref-target:", &arg)) { |
424 | 0 | *unborn_head_target = xstrdup(arg); |
425 | 0 | break; |
426 | 0 | } |
427 | 0 | } |
428 | 0 | } |
429 | 0 | goto out; |
430 | 0 | } |
431 | 0 | if (parse_oid_hex_algop(line_sections.items[i++].string, &old_oid, &end, reader->hash_algo) || |
432 | 0 | *end) { |
433 | 0 | ret = 0; |
434 | 0 | goto out; |
435 | 0 | } |
436 | | |
437 | 0 | ref = alloc_ref(line_sections.items[i++].string); |
438 | |
|
439 | 0 | memcpy(ref->old_oid.hash, old_oid.hash, reader->hash_algo->rawsz); |
440 | 0 | **list = ref; |
441 | 0 | *list = &ref->next; |
442 | |
|
443 | 0 | for (; i < line_sections.nr; i++) { |
444 | 0 | const char *arg = line_sections.items[i].string; |
445 | 0 | if (skip_prefix(arg, "symref-target:", &arg)) |
446 | 0 | ref->symref = xstrdup(arg); |
447 | |
|
448 | 0 | if (skip_prefix(arg, "peeled:", &arg)) { |
449 | 0 | struct object_id peeled_oid; |
450 | 0 | char *peeled_name; |
451 | 0 | struct ref *peeled; |
452 | 0 | if (parse_oid_hex_algop(arg, &peeled_oid, &end, |
453 | 0 | reader->hash_algo) || *end) { |
454 | 0 | ret = 0; |
455 | 0 | goto out; |
456 | 0 | } |
457 | | |
458 | 0 | peeled_name = xstrfmt("%s^{}", ref->name); |
459 | 0 | peeled = alloc_ref(peeled_name); |
460 | |
|
461 | 0 | memcpy(peeled->old_oid.hash, peeled_oid.hash, |
462 | 0 | reader->hash_algo->rawsz); |
463 | 0 | **list = peeled; |
464 | 0 | *list = &peeled->next; |
465 | |
|
466 | 0 | free(peeled_name); |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | 0 | out: |
471 | 0 | string_list_clear(&line_sections, 0); |
472 | 0 | return ret; |
473 | 0 | } |
474 | | |
475 | | void check_stateless_delimiter(int stateless_rpc, |
476 | | struct packet_reader *reader, |
477 | | const char *error) |
478 | 0 | { |
479 | 0 | if (!stateless_rpc) |
480 | 0 | return; /* not in stateless mode, no delimiter expected */ |
481 | 0 | if (packet_reader_read(reader) != PACKET_READ_RESPONSE_END) |
482 | 0 | die("%s", error); |
483 | 0 | } |
484 | | |
485 | | static void send_capabilities(int fd_out, struct packet_reader *reader) |
486 | 0 | { |
487 | 0 | const char *hash_name; |
488 | |
|
489 | 0 | if (server_supports_v2("agent")) |
490 | 0 | packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized()); |
491 | |
|
492 | 0 | if (server_feature_v2("object-format", &hash_name)) { |
493 | 0 | int hash_algo = hash_algo_by_name(hash_name); |
494 | 0 | if (hash_algo == GIT_HASH_UNKNOWN) |
495 | 0 | die(_("unknown object format '%s' specified by server"), hash_name); |
496 | 0 | reader->hash_algo = &hash_algos[hash_algo]; |
497 | 0 | packet_write_fmt(fd_out, "object-format=%s", reader->hash_algo->name); |
498 | 0 | } else { |
499 | 0 | reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; |
500 | 0 | } |
501 | 0 | } |
502 | | |
503 | | int get_remote_bundle_uri(int fd_out, struct packet_reader *reader, |
504 | | struct bundle_list *bundles, int stateless_rpc) |
505 | 0 | { |
506 | 0 | int line_nr = 1; |
507 | | |
508 | | /* Assert bundle-uri support */ |
509 | 0 | ensure_server_supports_v2("bundle-uri"); |
510 | | |
511 | | /* (Re-)send capabilities */ |
512 | 0 | send_capabilities(fd_out, reader); |
513 | | |
514 | | /* Send command */ |
515 | 0 | packet_write_fmt(fd_out, "command=bundle-uri\n"); |
516 | 0 | packet_delim(fd_out); |
517 | |
|
518 | 0 | packet_flush(fd_out); |
519 | | |
520 | | /* Process response from server */ |
521 | 0 | while (packet_reader_read(reader) == PACKET_READ_NORMAL) { |
522 | 0 | const char *line = reader->line; |
523 | 0 | line_nr++; |
524 | |
|
525 | 0 | if (!bundle_uri_parse_line(bundles, line)) |
526 | 0 | continue; |
527 | | |
528 | 0 | return error(_("error on bundle-uri response line %d: %s"), |
529 | 0 | line_nr, line); |
530 | 0 | } |
531 | | |
532 | 0 | if (reader->status != PACKET_READ_FLUSH) |
533 | 0 | return error(_("expected flush after bundle-uri listing")); |
534 | | |
535 | | /* |
536 | | * Might die(), but obscure enough that that's OK, e.g. in |
537 | | * serve.c we'll call BUG() on its equivalent (the |
538 | | * PACKET_READ_RESPONSE_END check). |
539 | | */ |
540 | 0 | check_stateless_delimiter(stateless_rpc, reader, |
541 | 0 | _("expected response end packet after ref listing")); |
542 | |
|
543 | 0 | return 0; |
544 | 0 | } |
545 | | |
546 | | struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, |
547 | | struct ref **list, int for_push, |
548 | | struct transport_ls_refs_options *transport_options, |
549 | | const struct string_list *server_options, |
550 | | int stateless_rpc) |
551 | 0 | { |
552 | 0 | int i; |
553 | 0 | struct strvec *ref_prefixes = transport_options ? |
554 | 0 | &transport_options->ref_prefixes : NULL; |
555 | 0 | const char **unborn_head_target = transport_options ? |
556 | 0 | &transport_options->unborn_head_target : NULL; |
557 | 0 | *list = NULL; |
558 | |
|
559 | 0 | ensure_server_supports_v2("ls-refs"); |
560 | 0 | packet_write_fmt(fd_out, "command=ls-refs\n"); |
561 | | |
562 | | /* Send capabilities */ |
563 | 0 | send_capabilities(fd_out, reader); |
564 | |
|
565 | 0 | if (server_options && server_options->nr) { |
566 | 0 | ensure_server_supports_v2("server-option"); |
567 | 0 | for (i = 0; i < server_options->nr; i++) |
568 | 0 | packet_write_fmt(fd_out, "server-option=%s", |
569 | 0 | server_options->items[i].string); |
570 | 0 | } |
571 | |
|
572 | 0 | packet_delim(fd_out); |
573 | | /* When pushing we don't want to request the peeled tags */ |
574 | 0 | if (!for_push) |
575 | 0 | packet_write_fmt(fd_out, "peel\n"); |
576 | 0 | packet_write_fmt(fd_out, "symrefs\n"); |
577 | 0 | if (server_supports_feature("ls-refs", "unborn", 0)) |
578 | 0 | packet_write_fmt(fd_out, "unborn\n"); |
579 | 0 | for (i = 0; ref_prefixes && i < ref_prefixes->nr; i++) { |
580 | 0 | packet_write_fmt(fd_out, "ref-prefix %s\n", |
581 | 0 | ref_prefixes->v[i]); |
582 | 0 | } |
583 | 0 | packet_flush(fd_out); |
584 | | |
585 | | /* Process response from server */ |
586 | 0 | while (packet_reader_read(reader) == PACKET_READ_NORMAL) { |
587 | 0 | if (!process_ref_v2(reader, &list, unborn_head_target)) |
588 | 0 | die(_("invalid ls-refs response: %s"), reader->line); |
589 | 0 | } |
590 | | |
591 | 0 | if (reader->status != PACKET_READ_FLUSH) |
592 | 0 | die(_("expected flush after ref listing")); |
593 | | |
594 | 0 | check_stateless_delimiter(stateless_rpc, reader, |
595 | 0 | _("expected response end packet after ref listing")); |
596 | |
|
597 | 0 | return list; |
598 | 0 | } |
599 | | |
600 | | const char *parse_feature_value(const char *feature_list, const char *feature, size_t *lenp, size_t *offset) |
601 | 0 | { |
602 | 0 | const char *orig_start = feature_list; |
603 | 0 | size_t len; |
604 | |
|
605 | 0 | if (!feature_list) |
606 | 0 | return NULL; |
607 | | |
608 | 0 | len = strlen(feature); |
609 | 0 | if (offset) |
610 | 0 | feature_list += *offset; |
611 | 0 | while (*feature_list) { |
612 | 0 | const char *found = strstr(feature_list, feature); |
613 | 0 | if (!found) |
614 | 0 | return NULL; |
615 | 0 | if (feature_list == found || isspace(found[-1])) { |
616 | 0 | const char *value = found + len; |
617 | | /* feature with no value (e.g., "thin-pack") */ |
618 | 0 | if (!*value || isspace(*value)) { |
619 | 0 | if (lenp) |
620 | 0 | *lenp = 0; |
621 | 0 | if (offset) |
622 | 0 | *offset = found + len - orig_start; |
623 | 0 | return value; |
624 | 0 | } |
625 | | /* feature with a value (e.g., "agent=git/1.2.3") */ |
626 | 0 | else if (*value == '=') { |
627 | 0 | size_t end; |
628 | |
|
629 | 0 | value++; |
630 | 0 | end = strcspn(value, " \t\n"); |
631 | 0 | if (lenp) |
632 | 0 | *lenp = end; |
633 | 0 | if (offset) |
634 | 0 | *offset = value + end - orig_start; |
635 | 0 | return value; |
636 | 0 | } |
637 | | /* |
638 | | * otherwise we matched a substring of another feature; |
639 | | * keep looking |
640 | | */ |
641 | 0 | } |
642 | 0 | feature_list = found + 1; |
643 | 0 | } |
644 | 0 | return NULL; |
645 | 0 | } |
646 | | |
647 | | int server_supports_hash(const char *desired, int *feature_supported) |
648 | 0 | { |
649 | 0 | size_t offset = 0; |
650 | 0 | size_t len; |
651 | 0 | const char *hash; |
652 | |
|
653 | 0 | hash = next_server_feature_value("object-format", &len, &offset); |
654 | 0 | if (feature_supported) |
655 | 0 | *feature_supported = !!hash; |
656 | 0 | if (!hash) { |
657 | 0 | hash = hash_algos[GIT_HASH_SHA1].name; |
658 | 0 | len = strlen(hash); |
659 | 0 | } |
660 | 0 | while (hash) { |
661 | 0 | if (!xstrncmpz(desired, hash, len)) |
662 | 0 | return 1; |
663 | | |
664 | 0 | hash = next_server_feature_value("object-format", &len, &offset); |
665 | 0 | } |
666 | 0 | return 0; |
667 | 0 | } |
668 | | |
669 | | int parse_feature_request(const char *feature_list, const char *feature) |
670 | 0 | { |
671 | 0 | return !!parse_feature_value(feature_list, feature, NULL, NULL); |
672 | 0 | } |
673 | | |
674 | | static const char *next_server_feature_value(const char *feature, size_t *len, size_t *offset) |
675 | 0 | { |
676 | 0 | return parse_feature_value(server_capabilities_v1, feature, len, offset); |
677 | 0 | } |
678 | | |
679 | | const char *server_feature_value(const char *feature, size_t *len) |
680 | 0 | { |
681 | 0 | return parse_feature_value(server_capabilities_v1, feature, len, NULL); |
682 | 0 | } |
683 | | |
684 | | int server_supports(const char *feature) |
685 | 0 | { |
686 | 0 | return !!server_feature_value(feature, NULL); |
687 | 0 | } |
688 | | |
689 | | enum protocol { |
690 | | PROTO_LOCAL = 1, |
691 | | PROTO_FILE, |
692 | | PROTO_SSH, |
693 | | PROTO_GIT |
694 | | }; |
695 | | |
696 | | int url_is_local_not_ssh(const char *url) |
697 | 0 | { |
698 | 0 | const char *colon = strchr(url, ':'); |
699 | 0 | const char *slash = strchr(url, '/'); |
700 | 0 | return !colon || (slash && slash < colon) || |
701 | 0 | (has_dos_drive_prefix(url) && is_valid_path(url)); |
702 | 0 | } |
703 | | |
704 | | static const char *prot_name(enum protocol protocol) |
705 | 0 | { |
706 | 0 | switch (protocol) { |
707 | 0 | case PROTO_LOCAL: |
708 | 0 | case PROTO_FILE: |
709 | 0 | return "file"; |
710 | 0 | case PROTO_SSH: |
711 | 0 | return "ssh"; |
712 | 0 | case PROTO_GIT: |
713 | 0 | return "git"; |
714 | 0 | default: |
715 | 0 | return "unknown protocol"; |
716 | 0 | } |
717 | 0 | } |
718 | | |
719 | | static enum protocol get_protocol(const char *name) |
720 | 0 | { |
721 | 0 | if (!strcmp(name, "ssh")) |
722 | 0 | return PROTO_SSH; |
723 | 0 | if (!strcmp(name, "git")) |
724 | 0 | return PROTO_GIT; |
725 | 0 | if (!strcmp(name, "git+ssh")) /* deprecated - do not use */ |
726 | 0 | return PROTO_SSH; |
727 | 0 | if (!strcmp(name, "ssh+git")) /* deprecated - do not use */ |
728 | 0 | return PROTO_SSH; |
729 | 0 | if (!strcmp(name, "file")) |
730 | 0 | return PROTO_FILE; |
731 | 0 | die(_("protocol '%s' is not supported"), name); |
732 | 0 | } |
733 | | |
734 | | static char *host_end(char **hoststart, int removebrackets) |
735 | 0 | { |
736 | 0 | char *host = *hoststart; |
737 | 0 | char *end; |
738 | 0 | char *start = strstr(host, "@["); |
739 | 0 | if (start) |
740 | 0 | start++; /* Jump over '@' */ |
741 | 0 | else |
742 | 0 | start = host; |
743 | 0 | if (start[0] == '[') { |
744 | 0 | end = strchr(start + 1, ']'); |
745 | 0 | if (end) { |
746 | 0 | if (removebrackets) { |
747 | 0 | *end = 0; |
748 | 0 | memmove(start, start + 1, end - start); |
749 | 0 | end++; |
750 | 0 | } |
751 | 0 | } else |
752 | 0 | end = host; |
753 | 0 | } else |
754 | 0 | end = host; |
755 | 0 | return end; |
756 | 0 | } |
757 | | |
758 | 0 | #define STR_(s) # s |
759 | 0 | #define STR(s) STR_(s) |
760 | | |
761 | | static void get_host_and_port(char **host, const char **port) |
762 | 0 | { |
763 | 0 | char *colon, *end; |
764 | 0 | end = host_end(host, 1); |
765 | 0 | colon = strchr(end, ':'); |
766 | 0 | if (colon) { |
767 | 0 | long portnr = strtol(colon + 1, &end, 10); |
768 | 0 | if (end != colon + 1 && *end == '\0' && 0 <= portnr && portnr < 65536) { |
769 | 0 | *colon = 0; |
770 | 0 | *port = colon + 1; |
771 | 0 | } else if (!colon[1]) { |
772 | 0 | *colon = 0; |
773 | 0 | } |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | | static void enable_keepalive(int sockfd) |
778 | 0 | { |
779 | 0 | int ka = 1; |
780 | |
|
781 | 0 | if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0) |
782 | 0 | error_errno(_("unable to set SO_KEEPALIVE on socket")); |
783 | 0 | } |
784 | | |
785 | | #ifndef NO_IPV6 |
786 | | |
787 | | static const char *ai_name(const struct addrinfo *ai) |
788 | 0 | { |
789 | 0 | static char addr[NI_MAXHOST]; |
790 | 0 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0, |
791 | 0 | NI_NUMERICHOST) != 0) |
792 | 0 | xsnprintf(addr, sizeof(addr), "(unknown)"); |
793 | |
|
794 | 0 | return addr; |
795 | 0 | } |
796 | | |
797 | | /* |
798 | | * Returns a connected socket() fd, or else die()s. |
799 | | */ |
800 | | static int git_tcp_connect_sock(char *host, int flags) |
801 | 0 | { |
802 | 0 | struct strbuf error_message = STRBUF_INIT; |
803 | 0 | int sockfd = -1; |
804 | 0 | const char *port = STR(DEFAULT_GIT_PORT); |
805 | 0 | struct addrinfo hints, *ai0, *ai; |
806 | 0 | int gai; |
807 | 0 | int cnt = 0; |
808 | |
|
809 | 0 | get_host_and_port(&host, &port); |
810 | 0 | if (!*port) |
811 | 0 | port = "<none>"; |
812 | |
|
813 | 0 | memset(&hints, 0, sizeof(hints)); |
814 | 0 | if (flags & CONNECT_IPV4) |
815 | 0 | hints.ai_family = AF_INET; |
816 | 0 | else if (flags & CONNECT_IPV6) |
817 | 0 | hints.ai_family = AF_INET6; |
818 | 0 | hints.ai_socktype = SOCK_STREAM; |
819 | 0 | hints.ai_protocol = IPPROTO_TCP; |
820 | |
|
821 | 0 | if (flags & CONNECT_VERBOSE) |
822 | 0 | fprintf(stderr, _("Looking up %s ... "), host); |
823 | |
|
824 | 0 | gai = getaddrinfo(host, port, &hints, &ai); |
825 | 0 | if (gai) |
826 | 0 | die(_("unable to look up %s (port %s) (%s)"), host, port, gai_strerror(gai)); |
827 | | |
828 | 0 | if (flags & CONNECT_VERBOSE) |
829 | | /* TRANSLATORS: this is the end of "Looking up %s ... " */ |
830 | 0 | fprintf(stderr, _("done.\nConnecting to %s (port %s) ... "), host, port); |
831 | |
|
832 | 0 | for (ai0 = ai; ai; ai = ai->ai_next, cnt++) { |
833 | 0 | sockfd = socket(ai->ai_family, |
834 | 0 | ai->ai_socktype, ai->ai_protocol); |
835 | 0 | if ((sockfd < 0) || |
836 | 0 | (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)) { |
837 | 0 | strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n", |
838 | 0 | host, cnt, ai_name(ai), strerror(errno)); |
839 | 0 | if (0 <= sockfd) |
840 | 0 | close(sockfd); |
841 | 0 | sockfd = -1; |
842 | 0 | continue; |
843 | 0 | } |
844 | 0 | if (flags & CONNECT_VERBOSE) |
845 | 0 | fprintf(stderr, "%s ", ai_name(ai)); |
846 | 0 | break; |
847 | 0 | } |
848 | |
|
849 | 0 | freeaddrinfo(ai0); |
850 | |
|
851 | 0 | if (sockfd < 0) |
852 | 0 | die(_("unable to connect to %s:\n%s"), host, error_message.buf); |
853 | | |
854 | 0 | enable_keepalive(sockfd); |
855 | |
|
856 | 0 | if (flags & CONNECT_VERBOSE) |
857 | | /* TRANSLATORS: this is the end of "Connecting to %s (port %s) ... " */ |
858 | 0 | fprintf_ln(stderr, _("done.")); |
859 | |
|
860 | 0 | strbuf_release(&error_message); |
861 | |
|
862 | 0 | return sockfd; |
863 | 0 | } |
864 | | |
865 | | #else /* NO_IPV6 */ |
866 | | |
867 | | /* |
868 | | * Returns a connected socket() fd, or else die()s. |
869 | | */ |
870 | | static int git_tcp_connect_sock(char *host, int flags) |
871 | | { |
872 | | struct strbuf error_message = STRBUF_INIT; |
873 | | int sockfd = -1; |
874 | | const char *port = STR(DEFAULT_GIT_PORT); |
875 | | char *ep; |
876 | | struct hostent *he; |
877 | | struct sockaddr_in sa; |
878 | | char **ap; |
879 | | unsigned int nport; |
880 | | int cnt; |
881 | | |
882 | | get_host_and_port(&host, &port); |
883 | | |
884 | | if (flags & CONNECT_VERBOSE) |
885 | | fprintf(stderr, _("Looking up %s ... "), host); |
886 | | |
887 | | he = gethostbyname(host); |
888 | | if (!he) |
889 | | die(_("unable to look up %s (%s)"), host, hstrerror(h_errno)); |
890 | | nport = strtoul(port, &ep, 10); |
891 | | if ( ep == port || *ep ) { |
892 | | /* Not numeric */ |
893 | | struct servent *se = getservbyname(port,"tcp"); |
894 | | if ( !se ) |
895 | | die(_("unknown port %s"), port); |
896 | | nport = se->s_port; |
897 | | } |
898 | | |
899 | | if (flags & CONNECT_VERBOSE) |
900 | | /* TRANSLATORS: this is the end of "Looking up %s ... " */ |
901 | | fprintf(stderr, _("done.\nConnecting to %s (port %s) ... "), host, port); |
902 | | |
903 | | for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) { |
904 | | memset(&sa, 0, sizeof sa); |
905 | | sa.sin_family = he->h_addrtype; |
906 | | sa.sin_port = htons(nport); |
907 | | memcpy(&sa.sin_addr, *ap, he->h_length); |
908 | | |
909 | | sockfd = socket(he->h_addrtype, SOCK_STREAM, 0); |
910 | | if ((sockfd < 0) || |
911 | | connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) { |
912 | | strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n", |
913 | | host, |
914 | | cnt, |
915 | | inet_ntoa(*(struct in_addr *)&sa.sin_addr), |
916 | | strerror(errno)); |
917 | | if (0 <= sockfd) |
918 | | close(sockfd); |
919 | | sockfd = -1; |
920 | | continue; |
921 | | } |
922 | | if (flags & CONNECT_VERBOSE) |
923 | | fprintf(stderr, "%s ", |
924 | | inet_ntoa(*(struct in_addr *)&sa.sin_addr)); |
925 | | break; |
926 | | } |
927 | | |
928 | | if (sockfd < 0) |
929 | | die(_("unable to connect to %s:\n%s"), host, error_message.buf); |
930 | | |
931 | | enable_keepalive(sockfd); |
932 | | |
933 | | if (flags & CONNECT_VERBOSE) |
934 | | /* TRANSLATORS: this is the end of "Connecting to %s (port %s) ... " */ |
935 | | fprintf_ln(stderr, _("done.")); |
936 | | |
937 | | return sockfd; |
938 | | } |
939 | | |
940 | | #endif /* NO_IPV6 */ |
941 | | |
942 | | |
943 | | /* |
944 | | * Dummy child_process returned by git_connect() if the transport protocol |
945 | | * does not need fork(2). |
946 | | */ |
947 | | static struct child_process no_fork = CHILD_PROCESS_INIT; |
948 | | |
949 | | int git_connection_is_socket(struct child_process *conn) |
950 | 0 | { |
951 | 0 | return conn == &no_fork; |
952 | 0 | } |
953 | | |
954 | | static struct child_process *git_tcp_connect(int fd[2], char *host, int flags) |
955 | 0 | { |
956 | 0 | int sockfd = git_tcp_connect_sock(host, flags); |
957 | |
|
958 | 0 | fd[0] = sockfd; |
959 | 0 | fd[1] = dup(sockfd); |
960 | |
|
961 | 0 | return &no_fork; |
962 | 0 | } |
963 | | |
964 | | |
965 | | static char *git_proxy_command; |
966 | | |
967 | | static int git_proxy_command_options(const char *var, const char *value, |
968 | | const struct config_context *ctx, void *cb) |
969 | 0 | { |
970 | 0 | if (!strcmp(var, "core.gitproxy")) { |
971 | 0 | const char *for_pos; |
972 | 0 | int matchlen = -1; |
973 | 0 | int hostlen; |
974 | 0 | const char *rhost_name = cb; |
975 | 0 | int rhost_len = strlen(rhost_name); |
976 | |
|
977 | 0 | if (git_proxy_command) |
978 | 0 | return 0; |
979 | 0 | if (!value) |
980 | 0 | return config_error_nonbool(var); |
981 | | /* [core] |
982 | | * ;# matches www.kernel.org as well |
983 | | * gitproxy = netcatter-1 for kernel.org |
984 | | * gitproxy = netcatter-2 for sample.xz |
985 | | * gitproxy = netcatter-default |
986 | | */ |
987 | 0 | for_pos = strstr(value, " for "); |
988 | 0 | if (!for_pos) |
989 | | /* matches everybody */ |
990 | 0 | matchlen = strlen(value); |
991 | 0 | else { |
992 | 0 | hostlen = strlen(for_pos + 5); |
993 | 0 | if (rhost_len < hostlen) |
994 | 0 | matchlen = -1; |
995 | 0 | else if (!strncmp(for_pos + 5, |
996 | 0 | rhost_name + rhost_len - hostlen, |
997 | 0 | hostlen) && |
998 | 0 | ((rhost_len == hostlen) || |
999 | 0 | rhost_name[rhost_len - hostlen -1] == '.')) |
1000 | 0 | matchlen = for_pos - value; |
1001 | 0 | else |
1002 | 0 | matchlen = -1; |
1003 | 0 | } |
1004 | 0 | if (0 <= matchlen) { |
1005 | | /* core.gitproxy = none for kernel.org */ |
1006 | 0 | if (matchlen == 4 && |
1007 | 0 | !memcmp(value, "none", 4)) |
1008 | 0 | matchlen = 0; |
1009 | 0 | git_proxy_command = xmemdupz(value, matchlen); |
1010 | 0 | } |
1011 | 0 | return 0; |
1012 | 0 | } |
1013 | | |
1014 | 0 | return git_default_config(var, value, ctx, cb); |
1015 | 0 | } |
1016 | | |
1017 | | static int git_use_proxy(const char *host) |
1018 | 0 | { |
1019 | 0 | git_proxy_command = getenv("GIT_PROXY_COMMAND"); |
1020 | 0 | git_config(git_proxy_command_options, (void*)host); |
1021 | 0 | return (git_proxy_command && *git_proxy_command); |
1022 | 0 | } |
1023 | | |
1024 | | static struct child_process *git_proxy_connect(int fd[2], char *host) |
1025 | 0 | { |
1026 | 0 | const char *port = STR(DEFAULT_GIT_PORT); |
1027 | 0 | struct child_process *proxy; |
1028 | |
|
1029 | 0 | get_host_and_port(&host, &port); |
1030 | |
|
1031 | 0 | if (looks_like_command_line_option(host)) |
1032 | 0 | die(_("strange hostname '%s' blocked"), host); |
1033 | 0 | if (looks_like_command_line_option(port)) |
1034 | 0 | die(_("strange port '%s' blocked"), port); |
1035 | | |
1036 | 0 | proxy = xmalloc(sizeof(*proxy)); |
1037 | 0 | child_process_init(proxy); |
1038 | 0 | strvec_push(&proxy->args, git_proxy_command); |
1039 | 0 | strvec_push(&proxy->args, host); |
1040 | 0 | strvec_push(&proxy->args, port); |
1041 | 0 | proxy->in = -1; |
1042 | 0 | proxy->out = -1; |
1043 | 0 | if (start_command(proxy)) |
1044 | 0 | die(_("cannot start proxy %s"), git_proxy_command); |
1045 | 0 | fd[0] = proxy->out; /* read from proxy stdout */ |
1046 | 0 | fd[1] = proxy->in; /* write to proxy stdin */ |
1047 | 0 | return proxy; |
1048 | 0 | } |
1049 | | |
1050 | | static char *get_port(char *host) |
1051 | 0 | { |
1052 | 0 | char *end; |
1053 | 0 | char *p = strchr(host, ':'); |
1054 | |
|
1055 | 0 | if (p) { |
1056 | 0 | long port = strtol(p + 1, &end, 10); |
1057 | 0 | if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) { |
1058 | 0 | *p = '\0'; |
1059 | 0 | return p+1; |
1060 | 0 | } |
1061 | 0 | } |
1062 | | |
1063 | 0 | return NULL; |
1064 | 0 | } |
1065 | | |
1066 | | /* |
1067 | | * Extract protocol and relevant parts from the specified connection URL. |
1068 | | * The caller must free() the returned strings. |
1069 | | */ |
1070 | | static enum protocol parse_connect_url(const char *url_orig, char **ret_host, |
1071 | | char **ret_path) |
1072 | 0 | { |
1073 | 0 | char *url; |
1074 | 0 | char *host, *path; |
1075 | 0 | char *end; |
1076 | 0 | int separator = '/'; |
1077 | 0 | enum protocol protocol = PROTO_LOCAL; |
1078 | |
|
1079 | 0 | if (is_url(url_orig)) |
1080 | 0 | url = url_decode(url_orig); |
1081 | 0 | else |
1082 | 0 | url = xstrdup(url_orig); |
1083 | |
|
1084 | 0 | host = strstr(url, "://"); |
1085 | 0 | if (host) { |
1086 | 0 | *host = '\0'; |
1087 | 0 | protocol = get_protocol(url); |
1088 | 0 | host += 3; |
1089 | 0 | } else { |
1090 | 0 | host = url; |
1091 | 0 | if (!url_is_local_not_ssh(url)) { |
1092 | 0 | protocol = PROTO_SSH; |
1093 | 0 | separator = ':'; |
1094 | 0 | } |
1095 | 0 | } |
1096 | | |
1097 | | /* |
1098 | | * Don't do destructive transforms as protocol code does |
1099 | | * '[]' unwrapping in get_host_and_port() |
1100 | | */ |
1101 | 0 | end = host_end(&host, 0); |
1102 | |
|
1103 | 0 | if (protocol == PROTO_LOCAL) |
1104 | 0 | path = end; |
1105 | 0 | else if (protocol == PROTO_FILE && *host != '/' && |
1106 | 0 | !has_dos_drive_prefix(host) && |
1107 | 0 | offset_1st_component(host - 2) > 1) |
1108 | 0 | path = host - 2; /* include the leading "//" */ |
1109 | 0 | else if (protocol == PROTO_FILE && has_dos_drive_prefix(end)) |
1110 | 0 | path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */ |
1111 | 0 | else |
1112 | 0 | path = strchr(end, separator); |
1113 | |
|
1114 | 0 | if (!path || !*path) |
1115 | 0 | die(_("no path specified; see 'git help pull' for valid url syntax")); |
1116 | | |
1117 | | /* |
1118 | | * null-terminate hostname and point path to ~ for URL's like this: |
1119 | | * ssh://host.xz/~user/repo |
1120 | | */ |
1121 | | |
1122 | 0 | end = path; /* Need to \0 terminate host here */ |
1123 | 0 | if (separator == ':') |
1124 | 0 | path++; /* path starts after ':' */ |
1125 | 0 | if (protocol == PROTO_GIT || protocol == PROTO_SSH) { |
1126 | 0 | if (path[1] == '~') |
1127 | 0 | path++; |
1128 | 0 | } |
1129 | |
|
1130 | 0 | path = xstrdup(path); |
1131 | 0 | *end = '\0'; |
1132 | |
|
1133 | 0 | *ret_host = xstrdup(host); |
1134 | 0 | *ret_path = path; |
1135 | 0 | free(url); |
1136 | 0 | return protocol; |
1137 | 0 | } |
1138 | | |
1139 | | static const char *get_ssh_command(void) |
1140 | 0 | { |
1141 | 0 | const char *ssh; |
1142 | |
|
1143 | 0 | if ((ssh = getenv("GIT_SSH_COMMAND"))) |
1144 | 0 | return ssh; |
1145 | | |
1146 | 0 | if (!git_config_get_string_tmp("core.sshcommand", &ssh)) |
1147 | 0 | return ssh; |
1148 | | |
1149 | 0 | return NULL; |
1150 | 0 | } |
1151 | | |
1152 | | enum ssh_variant { |
1153 | | VARIANT_AUTO, |
1154 | | VARIANT_SIMPLE, |
1155 | | VARIANT_SSH, |
1156 | | VARIANT_PLINK, |
1157 | | VARIANT_PUTTY, |
1158 | | VARIANT_TORTOISEPLINK, |
1159 | | }; |
1160 | | |
1161 | | static void override_ssh_variant(enum ssh_variant *ssh_variant) |
1162 | 0 | { |
1163 | 0 | const char *variant = getenv("GIT_SSH_VARIANT"); |
1164 | |
|
1165 | 0 | if (!variant && git_config_get_string_tmp("ssh.variant", &variant)) |
1166 | 0 | return; |
1167 | | |
1168 | 0 | if (!strcmp(variant, "auto")) |
1169 | 0 | *ssh_variant = VARIANT_AUTO; |
1170 | 0 | else if (!strcmp(variant, "plink")) |
1171 | 0 | *ssh_variant = VARIANT_PLINK; |
1172 | 0 | else if (!strcmp(variant, "putty")) |
1173 | 0 | *ssh_variant = VARIANT_PUTTY; |
1174 | 0 | else if (!strcmp(variant, "tortoiseplink")) |
1175 | 0 | *ssh_variant = VARIANT_TORTOISEPLINK; |
1176 | 0 | else if (!strcmp(variant, "simple")) |
1177 | 0 | *ssh_variant = VARIANT_SIMPLE; |
1178 | 0 | else |
1179 | 0 | *ssh_variant = VARIANT_SSH; |
1180 | 0 | } |
1181 | | |
1182 | | static enum ssh_variant determine_ssh_variant(const char *ssh_command, |
1183 | | int is_cmdline) |
1184 | 0 | { |
1185 | 0 | enum ssh_variant ssh_variant = VARIANT_AUTO; |
1186 | 0 | const char *variant; |
1187 | 0 | char *p = NULL; |
1188 | |
|
1189 | 0 | override_ssh_variant(&ssh_variant); |
1190 | |
|
1191 | 0 | if (ssh_variant != VARIANT_AUTO) |
1192 | 0 | return ssh_variant; |
1193 | | |
1194 | 0 | if (!is_cmdline) { |
1195 | 0 | p = xstrdup(ssh_command); |
1196 | 0 | variant = basename(p); |
1197 | 0 | } else { |
1198 | 0 | const char **ssh_argv; |
1199 | |
|
1200 | 0 | p = xstrdup(ssh_command); |
1201 | 0 | if (split_cmdline(p, &ssh_argv) > 0) { |
1202 | 0 | variant = basename((char *)ssh_argv[0]); |
1203 | | /* |
1204 | | * At this point, variant points into the buffer |
1205 | | * referenced by p, hence we do not need ssh_argv |
1206 | | * any longer. |
1207 | | */ |
1208 | 0 | free(ssh_argv); |
1209 | 0 | } else { |
1210 | 0 | free(p); |
1211 | 0 | return ssh_variant; |
1212 | 0 | } |
1213 | 0 | } |
1214 | | |
1215 | 0 | if (!strcasecmp(variant, "ssh") || |
1216 | 0 | !strcasecmp(variant, "ssh.exe")) |
1217 | 0 | ssh_variant = VARIANT_SSH; |
1218 | 0 | else if (!strcasecmp(variant, "plink") || |
1219 | 0 | !strcasecmp(variant, "plink.exe")) |
1220 | 0 | ssh_variant = VARIANT_PLINK; |
1221 | 0 | else if (!strcasecmp(variant, "tortoiseplink") || |
1222 | 0 | !strcasecmp(variant, "tortoiseplink.exe")) |
1223 | 0 | ssh_variant = VARIANT_TORTOISEPLINK; |
1224 | |
|
1225 | 0 | free(p); |
1226 | 0 | return ssh_variant; |
1227 | 0 | } |
1228 | | |
1229 | | /* |
1230 | | * Open a connection using Git's native protocol. |
1231 | | * |
1232 | | * The caller is responsible for freeing hostandport, but this function may |
1233 | | * modify it (for example, to truncate it to remove the port part). |
1234 | | */ |
1235 | | static struct child_process *git_connect_git(int fd[2], char *hostandport, |
1236 | | const char *path, const char *prog, |
1237 | | enum protocol_version version, |
1238 | | int flags) |
1239 | 0 | { |
1240 | 0 | struct child_process *conn; |
1241 | 0 | struct strbuf request = STRBUF_INIT; |
1242 | | /* |
1243 | | * Set up virtual host information based on where we will |
1244 | | * connect, unless the user has overridden us in |
1245 | | * the environment. |
1246 | | */ |
1247 | 0 | char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST"); |
1248 | 0 | if (target_host) |
1249 | 0 | target_host = xstrdup(target_host); |
1250 | 0 | else |
1251 | 0 | target_host = xstrdup(hostandport); |
1252 | |
|
1253 | 0 | transport_check_allowed("git"); |
1254 | 0 | if (strchr(target_host, '\n') || strchr(path, '\n')) |
1255 | 0 | die(_("newline is forbidden in git:// hosts and repo paths")); |
1256 | | |
1257 | | /* |
1258 | | * These underlying connection commands die() if they |
1259 | | * cannot connect. |
1260 | | */ |
1261 | 0 | if (git_use_proxy(hostandport)) |
1262 | 0 | conn = git_proxy_connect(fd, hostandport); |
1263 | 0 | else |
1264 | 0 | conn = git_tcp_connect(fd, hostandport, flags); |
1265 | | /* |
1266 | | * Separate original protocol components prog and path |
1267 | | * from extended host header with a NUL byte. |
1268 | | * |
1269 | | * Note: Do not add any other headers here! Doing so |
1270 | | * will cause older git-daemon servers to crash. |
1271 | | */ |
1272 | 0 | strbuf_addf(&request, |
1273 | 0 | "%s %s%chost=%s%c", |
1274 | 0 | prog, path, 0, |
1275 | 0 | target_host, 0); |
1276 | | |
1277 | | /* If using a new version put that stuff here after a second null byte */ |
1278 | 0 | if (version > 0) { |
1279 | 0 | strbuf_addch(&request, '\0'); |
1280 | 0 | strbuf_addf(&request, "version=%d%c", |
1281 | 0 | version, '\0'); |
1282 | 0 | } |
1283 | |
|
1284 | 0 | packet_write(fd[1], request.buf, request.len); |
1285 | |
|
1286 | 0 | free(target_host); |
1287 | 0 | strbuf_release(&request); |
1288 | 0 | return conn; |
1289 | 0 | } |
1290 | | |
1291 | | /* |
1292 | | * Append the appropriate environment variables to `env` and options to |
1293 | | * `args` for running ssh in Git's SSH-tunneled transport. |
1294 | | */ |
1295 | | static void push_ssh_options(struct strvec *args, struct strvec *env, |
1296 | | enum ssh_variant variant, const char *port, |
1297 | | enum protocol_version version, int flags) |
1298 | 0 | { |
1299 | 0 | if (variant == VARIANT_SSH && |
1300 | 0 | version > 0) { |
1301 | 0 | strvec_push(args, "-o"); |
1302 | 0 | strvec_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT); |
1303 | 0 | strvec_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d", |
1304 | 0 | version); |
1305 | 0 | } |
1306 | |
|
1307 | 0 | if (flags & CONNECT_IPV4) { |
1308 | 0 | switch (variant) { |
1309 | 0 | case VARIANT_AUTO: |
1310 | 0 | BUG("VARIANT_AUTO passed to push_ssh_options"); |
1311 | 0 | case VARIANT_SIMPLE: |
1312 | 0 | die(_("ssh variant 'simple' does not support -4")); |
1313 | 0 | case VARIANT_SSH: |
1314 | 0 | case VARIANT_PLINK: |
1315 | 0 | case VARIANT_PUTTY: |
1316 | 0 | case VARIANT_TORTOISEPLINK: |
1317 | 0 | strvec_push(args, "-4"); |
1318 | 0 | } |
1319 | 0 | } else if (flags & CONNECT_IPV6) { |
1320 | 0 | switch (variant) { |
1321 | 0 | case VARIANT_AUTO: |
1322 | 0 | BUG("VARIANT_AUTO passed to push_ssh_options"); |
1323 | 0 | case VARIANT_SIMPLE: |
1324 | 0 | die(_("ssh variant 'simple' does not support -6")); |
1325 | 0 | case VARIANT_SSH: |
1326 | 0 | case VARIANT_PLINK: |
1327 | 0 | case VARIANT_PUTTY: |
1328 | 0 | case VARIANT_TORTOISEPLINK: |
1329 | 0 | strvec_push(args, "-6"); |
1330 | 0 | } |
1331 | 0 | } |
1332 | | |
1333 | 0 | if (variant == VARIANT_TORTOISEPLINK) |
1334 | 0 | strvec_push(args, "-batch"); |
1335 | |
|
1336 | 0 | if (port) { |
1337 | 0 | switch (variant) { |
1338 | 0 | case VARIANT_AUTO: |
1339 | 0 | BUG("VARIANT_AUTO passed to push_ssh_options"); |
1340 | 0 | case VARIANT_SIMPLE: |
1341 | 0 | die(_("ssh variant 'simple' does not support setting port")); |
1342 | 0 | case VARIANT_SSH: |
1343 | 0 | strvec_push(args, "-p"); |
1344 | 0 | break; |
1345 | 0 | case VARIANT_PLINK: |
1346 | 0 | case VARIANT_PUTTY: |
1347 | 0 | case VARIANT_TORTOISEPLINK: |
1348 | 0 | strvec_push(args, "-P"); |
1349 | 0 | } |
1350 | | |
1351 | 0 | strvec_push(args, port); |
1352 | 0 | } |
1353 | 0 | } |
1354 | | |
1355 | | /* Prepare a child_process for use by Git's SSH-tunneled transport. */ |
1356 | | static void fill_ssh_args(struct child_process *conn, const char *ssh_host, |
1357 | | const char *port, enum protocol_version version, |
1358 | | int flags) |
1359 | 0 | { |
1360 | 0 | const char *ssh; |
1361 | 0 | enum ssh_variant variant; |
1362 | |
|
1363 | 0 | if (looks_like_command_line_option(ssh_host)) |
1364 | 0 | die(_("strange hostname '%s' blocked"), ssh_host); |
1365 | | |
1366 | 0 | ssh = get_ssh_command(); |
1367 | 0 | if (ssh) { |
1368 | 0 | variant = determine_ssh_variant(ssh, 1); |
1369 | 0 | } else { |
1370 | | /* |
1371 | | * GIT_SSH is the no-shell version of |
1372 | | * GIT_SSH_COMMAND (and must remain so for |
1373 | | * historical compatibility). |
1374 | | */ |
1375 | 0 | conn->use_shell = 0; |
1376 | |
|
1377 | 0 | ssh = getenv("GIT_SSH"); |
1378 | 0 | if (!ssh) |
1379 | 0 | ssh = "ssh"; |
1380 | 0 | variant = determine_ssh_variant(ssh, 0); |
1381 | 0 | } |
1382 | |
|
1383 | 0 | if (variant == VARIANT_AUTO) { |
1384 | 0 | struct child_process detect = CHILD_PROCESS_INIT; |
1385 | |
|
1386 | 0 | detect.use_shell = conn->use_shell; |
1387 | 0 | detect.no_stdin = detect.no_stdout = detect.no_stderr = 1; |
1388 | |
|
1389 | 0 | strvec_push(&detect.args, ssh); |
1390 | 0 | strvec_push(&detect.args, "-G"); |
1391 | 0 | push_ssh_options(&detect.args, &detect.env, |
1392 | 0 | VARIANT_SSH, port, version, flags); |
1393 | 0 | strvec_push(&detect.args, ssh_host); |
1394 | |
|
1395 | 0 | variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH; |
1396 | 0 | } |
1397 | |
|
1398 | 0 | strvec_push(&conn->args, ssh); |
1399 | 0 | push_ssh_options(&conn->args, &conn->env, variant, port, version, |
1400 | 0 | flags); |
1401 | 0 | strvec_push(&conn->args, ssh_host); |
1402 | 0 | } |
1403 | | |
1404 | | /* |
1405 | | * This returns the dummy child_process `no_fork` if the transport protocol |
1406 | | * does not need fork(2), or a struct child_process object if it does. Once |
1407 | | * done, finish the connection with finish_connect() with the value returned |
1408 | | * from this function (it is safe to call finish_connect() with NULL to |
1409 | | * support the former case). |
1410 | | * |
1411 | | * If it returns, the connect is successful; it just dies on errors (this |
1412 | | * will hopefully be changed in a libification effort, to return NULL when |
1413 | | * the connection failed). |
1414 | | */ |
1415 | | struct child_process *git_connect(int fd[2], const char *url, |
1416 | | const char *name, |
1417 | | const char *prog, int flags) |
1418 | 0 | { |
1419 | 0 | char *hostandport, *path; |
1420 | 0 | struct child_process *conn; |
1421 | 0 | enum protocol protocol; |
1422 | 0 | enum protocol_version version = get_protocol_version_config(); |
1423 | | |
1424 | | /* |
1425 | | * NEEDSWORK: If we are trying to use protocol v2 and we are planning |
1426 | | * to perform any operation that doesn't involve upload-pack (i.e., a |
1427 | | * fetch, ls-remote, etc), then fallback to v0 since we don't know how |
1428 | | * to do anything else (like push or remote archive) via v2. |
1429 | | */ |
1430 | 0 | if (version == protocol_v2 && strcmp("git-upload-pack", name)) |
1431 | 0 | version = protocol_v0; |
1432 | | |
1433 | | /* Without this we cannot rely on waitpid() to tell |
1434 | | * what happened to our children. |
1435 | | */ |
1436 | 0 | signal(SIGCHLD, SIG_DFL); |
1437 | |
|
1438 | 0 | protocol = parse_connect_url(url, &hostandport, &path); |
1439 | 0 | if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) { |
1440 | 0 | printf("Diag: url=%s\n", url ? url : "NULL"); |
1441 | 0 | printf("Diag: protocol=%s\n", prot_name(protocol)); |
1442 | 0 | printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL"); |
1443 | 0 | printf("Diag: path=%s\n", path ? path : "NULL"); |
1444 | 0 | conn = NULL; |
1445 | 0 | } else if (protocol == PROTO_GIT) { |
1446 | 0 | conn = git_connect_git(fd, hostandport, path, prog, version, flags); |
1447 | 0 | conn->trace2_child_class = "transport/git"; |
1448 | 0 | } else { |
1449 | 0 | struct strbuf cmd = STRBUF_INIT; |
1450 | 0 | const char *const *var; |
1451 | |
|
1452 | 0 | conn = xmalloc(sizeof(*conn)); |
1453 | 0 | child_process_init(conn); |
1454 | |
|
1455 | 0 | if (looks_like_command_line_option(path)) |
1456 | 0 | die(_("strange pathname '%s' blocked"), path); |
1457 | | |
1458 | 0 | strbuf_addstr(&cmd, prog); |
1459 | 0 | strbuf_addch(&cmd, ' '); |
1460 | 0 | sq_quote_buf(&cmd, path); |
1461 | | |
1462 | | /* remove repo-local variables from the environment */ |
1463 | 0 | for (var = local_repo_env; *var; var++) |
1464 | 0 | strvec_push(&conn->env, *var); |
1465 | |
|
1466 | 0 | conn->use_shell = 1; |
1467 | 0 | conn->in = conn->out = -1; |
1468 | 0 | if (protocol == PROTO_SSH) { |
1469 | 0 | char *ssh_host = hostandport; |
1470 | 0 | const char *port = NULL; |
1471 | 0 | transport_check_allowed("ssh"); |
1472 | 0 | get_host_and_port(&ssh_host, &port); |
1473 | |
|
1474 | 0 | if (!port) |
1475 | 0 | port = get_port(ssh_host); |
1476 | |
|
1477 | 0 | if (flags & CONNECT_DIAG_URL) { |
1478 | 0 | printf("Diag: url=%s\n", url ? url : "NULL"); |
1479 | 0 | printf("Diag: protocol=%s\n", prot_name(protocol)); |
1480 | 0 | printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL"); |
1481 | 0 | printf("Diag: port=%s\n", port ? port : "NONE"); |
1482 | 0 | printf("Diag: path=%s\n", path ? path : "NULL"); |
1483 | |
|
1484 | 0 | free(hostandport); |
1485 | 0 | free(path); |
1486 | 0 | free(conn); |
1487 | 0 | strbuf_release(&cmd); |
1488 | 0 | return NULL; |
1489 | 0 | } |
1490 | 0 | conn->trace2_child_class = "transport/ssh"; |
1491 | 0 | fill_ssh_args(conn, ssh_host, port, version, flags); |
1492 | 0 | } else { |
1493 | 0 | transport_check_allowed("file"); |
1494 | 0 | conn->trace2_child_class = "transport/file"; |
1495 | 0 | if (version > 0) { |
1496 | 0 | strvec_pushf(&conn->env, |
1497 | 0 | GIT_PROTOCOL_ENVIRONMENT "=version=%d", |
1498 | 0 | version); |
1499 | 0 | } |
1500 | 0 | } |
1501 | 0 | strvec_push(&conn->args, cmd.buf); |
1502 | |
|
1503 | 0 | if (start_command(conn)) |
1504 | 0 | die(_("unable to fork")); |
1505 | | |
1506 | 0 | fd[0] = conn->out; /* read from child's stdout */ |
1507 | 0 | fd[1] = conn->in; /* write to child's stdin */ |
1508 | 0 | strbuf_release(&cmd); |
1509 | 0 | } |
1510 | 0 | free(hostandport); |
1511 | 0 | free(path); |
1512 | 0 | return conn; |
1513 | 0 | } |
1514 | | |
1515 | | int finish_connect(struct child_process *conn) |
1516 | 0 | { |
1517 | 0 | int code; |
1518 | 0 | if (!conn || git_connection_is_socket(conn)) |
1519 | 0 | return 0; |
1520 | | |
1521 | 0 | code = finish_command(conn); |
1522 | 0 | free(conn); |
1523 | 0 | return code; |
1524 | 0 | } |