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