TransportGitAnon.java

  1. /*
  2.  * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  3.  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  4.  * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
  5.  *
  6.  * This program and the accompanying materials are made available under the
  7.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  8.  * https://www.eclipse.org/org/documents/edl-v10.php.
  9.  *
  10.  * SPDX-License-Identifier: BSD-3-Clause
  11.  */

  12. package org.eclipse.jgit.transport;

  13. import java.io.BufferedInputStream;
  14. import java.io.BufferedOutputStream;
  15. import java.io.IOException;
  16. import java.io.InputStream;
  17. import java.io.OutputStream;
  18. import java.net.ConnectException;
  19. import java.net.InetAddress;
  20. import java.net.InetSocketAddress;
  21. import java.net.Socket;
  22. import java.net.UnknownHostException;
  23. import java.util.Collection;
  24. import java.util.Collections;
  25. import java.util.EnumSet;
  26. import java.util.Set;

  27. import org.eclipse.jgit.errors.NotSupportedException;
  28. import org.eclipse.jgit.errors.TransportException;
  29. import org.eclipse.jgit.internal.JGitText;
  30. import org.eclipse.jgit.lib.Repository;

  31. /**
  32.  * Transport through a git-daemon waiting for anonymous TCP connections.
  33.  * <p>
  34.  * This transport supports the <code>git://</code> protocol, usually run on
  35.  * the IANA registered port 9418. It is a popular means for distributing open
  36.  * source projects, as there are no authentication or authorization overheads.
  37.  */
  38. class TransportGitAnon extends TcpTransport implements PackTransport {
  39.     static final int GIT_PORT = Daemon.DEFAULT_PORT;

  40.     static final TransportProtocol PROTO_GIT = new TransportProtocol() {
  41.         @Override
  42.         public String getName() {
  43.             return JGitText.get().transportProtoGitAnon;
  44.         }

  45.         @Override
  46.         public Set<String> getSchemes() {
  47.             return Collections.singleton("git"); //$NON-NLS-1$
  48.         }

  49.         @Override
  50.         public Set<URIishField> getRequiredFields() {
  51.             return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
  52.                     URIishField.PATH));
  53.         }

  54.         @Override
  55.         public Set<URIishField> getOptionalFields() {
  56.             return Collections.unmodifiableSet(EnumSet.of(URIishField.PORT));
  57.         }

  58.         @Override
  59.         public int getDefaultPort() {
  60.             return GIT_PORT;
  61.         }

  62.         @Override
  63.         public Transport open(URIish uri, Repository local, String remoteName)
  64.                 throws NotSupportedException {
  65.             return new TransportGitAnon(local, uri);
  66.         }

  67.         @Override
  68.         public Transport open(URIish uri) throws NotSupportedException, TransportException {
  69.             return new TransportGitAnon(uri);
  70.         }
  71.     };

  72.     TransportGitAnon(Repository local, URIish uri) {
  73.         super(local, uri);
  74.     }

  75.     TransportGitAnon(URIish uri) {
  76.         super(uri);
  77.     }

  78.     /** {@inheritDoc} */
  79.     @Override
  80.     public FetchConnection openFetch() throws TransportException {
  81.         return new TcpFetchConnection();
  82.     }

  83.     @Override
  84.     public FetchConnection openFetch(Collection<RefSpec> refSpecs,
  85.             String... additionalPatterns)
  86.             throws NotSupportedException, TransportException {
  87.         return new TcpFetchConnection(refSpecs, additionalPatterns);
  88.     }

  89.     /** {@inheritDoc} */
  90.     @Override
  91.     public PushConnection openPush() throws TransportException {
  92.         return new TcpPushConnection();
  93.     }

  94.     /** {@inheritDoc} */
  95.     @Override
  96.     public void close() {
  97.         // Resources must be established per-connection.
  98.     }

  99.     Socket openConnection() throws TransportException {
  100.         final int tms = getTimeout() > 0 ? getTimeout() * 1000 : 0;
  101.         final int port = uri.getPort() > 0 ? uri.getPort() : GIT_PORT;
  102.         @SuppressWarnings("resource") // Closed by the caller
  103.         final Socket s = new Socket();
  104.         try {
  105.             final InetAddress host = InetAddress.getByName(uri.getHost());
  106.             s.connect(new InetSocketAddress(host, port), tms);
  107.         } catch (IOException c) {
  108.             try {
  109.                 s.close();
  110.             } catch (IOException closeErr) {
  111.                 // ignore a failure during close, we're already failing
  112.             }
  113.             if (c instanceof UnknownHostException)
  114.                 throw new TransportException(uri, JGitText.get().unknownHost);
  115.             if (c instanceof ConnectException)
  116.                 throw new TransportException(uri, c.getMessage());
  117.             throw new TransportException(uri, c.getMessage(), c);
  118.         }
  119.         return s;
  120.     }

  121.     void service(String name, PacketLineOut pckOut,
  122.             TransferConfig.ProtocolVersion gitProtocol)
  123.             throws IOException {
  124.         final StringBuilder cmd = new StringBuilder();
  125.         cmd.append(name);
  126.         cmd.append(' ');
  127.         cmd.append(uri.getPath());
  128.         cmd.append('\0');
  129.         cmd.append("host="); //$NON-NLS-1$
  130.         cmd.append(uri.getHost());
  131.         if (uri.getPort() > 0 && uri.getPort() != GIT_PORT) {
  132.             cmd.append(":"); //$NON-NLS-1$
  133.             cmd.append(uri.getPort());
  134.         }
  135.         cmd.append('\0');
  136.         if (TransferConfig.ProtocolVersion.V2.equals(gitProtocol)) {
  137.             cmd.append('\0');
  138.             cmd.append(GitProtocolConstants.VERSION_2_REQUEST);
  139.             cmd.append('\0');
  140.         }
  141.         pckOut.writeString(cmd.toString());
  142.         pckOut.flush();
  143.     }

  144.     class TcpFetchConnection extends BasePackFetchConnection {
  145.         private Socket sock;

  146.         TcpFetchConnection() throws TransportException {
  147.             this(Collections.emptyList());
  148.         }

  149.         TcpFetchConnection(Collection<RefSpec> refSpecs,
  150.                 String... additionalPatterns) throws TransportException {
  151.             super(TransportGitAnon.this);
  152.             sock = openConnection();
  153.             try {
  154.                 InputStream sIn = sock.getInputStream();
  155.                 OutputStream sOut = sock.getOutputStream();

  156.                 sIn = new BufferedInputStream(sIn);
  157.                 sOut = new BufferedOutputStream(sOut);

  158.                 init(sIn, sOut);
  159.                 TransferConfig.ProtocolVersion gitProtocol = protocol;
  160.                 if (gitProtocol == null) {
  161.                     gitProtocol = TransferConfig.ProtocolVersion.V2;
  162.                 }
  163.                 service("git-upload-pack", pckOut, gitProtocol); //$NON-NLS-1$
  164.             } catch (IOException err) {
  165.                 close();
  166.                 throw new TransportException(uri,
  167.                         JGitText.get().remoteHungUpUnexpectedly, err);
  168.             }
  169.             if (!readAdvertisedRefs()) {
  170.                 lsRefs(refSpecs, additionalPatterns);
  171.             }
  172.         }

  173.         @Override
  174.         public void close() {
  175.             super.close();

  176.             if (sock != null) {
  177.                 try {
  178.                     sock.close();
  179.                 } catch (IOException err) {
  180.                     // Ignore errors during close.
  181.                 } finally {
  182.                     sock = null;
  183.                 }
  184.             }
  185.         }
  186.     }

  187.     class TcpPushConnection extends BasePackPushConnection {
  188.         private Socket sock;

  189.         TcpPushConnection() throws TransportException {
  190.             super(TransportGitAnon.this);
  191.             sock = openConnection();
  192.             try {
  193.                 InputStream sIn = sock.getInputStream();
  194.                 OutputStream sOut = sock.getOutputStream();

  195.                 sIn = new BufferedInputStream(sIn);
  196.                 sOut = new BufferedOutputStream(sOut);

  197.                 init(sIn, sOut);
  198.                 service("git-receive-pack", pckOut, null); //$NON-NLS-1$
  199.             } catch (IOException err) {
  200.                 close();
  201.                 throw new TransportException(uri,
  202.                         JGitText.get().remoteHungUpUnexpectedly, err);
  203.             }
  204.             readAdvertisedRefs();
  205.         }

  206.         @Override
  207.         public void close() {
  208.             super.close();

  209.             if (sock != null) {
  210.                 try {
  211.                     sock.close();
  212.                 } catch (IOException err) {
  213.                     // Ignore errors during close.
  214.                 } finally {
  215.                     sock = null;
  216.                 }
  217.             }
  218.         }
  219.     }
  220. }