blob: d7ba415ec873f87428875677de21a9adb76a2259 [file] [log] [blame]
Jens Geyer4c835952013-08-13 21:34:17 +02001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package thrift
21
22import (
John Boiles57852792018-01-05 14:37:05 -080023 "context"
Jens Geyer1e7971c2014-11-25 21:43:54 +010024 "crypto/tls"
Jens Geyer4c835952013-08-13 21:34:17 +020025 "net"
26 "time"
Jens Geyer4c835952013-08-13 21:34:17 +020027)
28
29type TSSLSocket struct {
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -070030 conn *socketConn
Jens Geyer00a4e3e2015-02-27 23:06:07 +010031 // hostPort contains host:port (e.g. "asdf.com:12345"). The field is
32 // only valid if addr is nil.
33 hostPort string
34 // addr is nil when hostPort is not "", and is only used when the
35 // TSSLSocket is constructed from a net.Addr.
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -070036 addr net.Addr
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -070037
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080038 cfg *TConfiguration
Jens Geyer4c835952013-08-13 21:34:17 +020039}
40
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080041// NewTSSLSocketConf creates a net.Conn-backed TTransport, given a host and port.
Jens Geyer4c835952013-08-13 21:34:17 +020042//
43// Example:
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080044//
Yuxuan 'fishy' Wang17373a32021-08-26 11:04:27 -070045// trans := thrift.NewTSSLSocketConf("localhost:9090", &TConfiguration{
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080046// ConnectTimeout: time.Second, // Use 0 for no timeout
47// SocketTimeout: time.Second, // Use 0 for no timeout
Yuxuan 'fishy' Wang17373a32021-08-26 11:04:27 -070048//
49// TLSConfig: &tls.Config{
50// // Fill in tls config here.
51// }
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080052// })
Yuxuan 'fishy' Wang17373a32021-08-26 11:04:27 -070053func NewTSSLSocketConf(hostPort string, conf *TConfiguration) *TSSLSocket {
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080054 if cfg := conf.GetTLSConfig(); cfg != nil && cfg.MinVersion == 0 {
James E. King, III06190872017-02-20 08:52:11 -050055 cfg.MinVersion = tls.VersionTLS10
56 }
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -070057 return &TSSLSocket{
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080058 hostPort: hostPort,
59 cfg: conf,
Yuxuan 'fishy' Wang17373a32021-08-26 11:04:27 -070060 }
Jens Geyer4c835952013-08-13 21:34:17 +020061}
62
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080063// Deprecated: Use NewTSSLSocketConf instead.
64func NewTSSLSocket(hostPort string, cfg *tls.Config) (*TSSLSocket, error) {
65 return NewTSSLSocketConf(hostPort, &TConfiguration{
66 TLSConfig: cfg,
67
68 noPropagation: true,
Yuxuan 'fishy' Wang17373a32021-08-26 11:04:27 -070069 }), nil
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080070}
71
72// Deprecated: Use NewTSSLSocketConf instead.
73func NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, connectTimeout, socketTimeout time.Duration) (*TSSLSocket, error) {
74 return NewTSSLSocketConf(hostPort, &TConfiguration{
75 ConnectTimeout: connectTimeout,
76 SocketTimeout: socketTimeout,
77 TLSConfig: cfg,
78
79 noPropagation: true,
Yuxuan 'fishy' Wang17373a32021-08-26 11:04:27 -070080 }), nil
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080081}
82
83// NewTSSLSocketFromAddrConf creates a TSSLSocket from a net.Addr.
84func NewTSSLSocketFromAddrConf(addr net.Addr, conf *TConfiguration) *TSSLSocket {
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -070085 return &TSSLSocket{
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080086 addr: addr,
87 cfg: conf,
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -070088 }
Jens Geyer4c835952013-08-13 21:34:17 +020089}
90
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080091// Deprecated: Use NewTSSLSocketFromAddrConf instead.
92func NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, connectTimeout, socketTimeout time.Duration) *TSSLSocket {
93 return NewTSSLSocketFromAddrConf(addr, &TConfiguration{
94 ConnectTimeout: connectTimeout,
95 SocketTimeout: socketTimeout,
96 TLSConfig: cfg,
97
98 noPropagation: true,
99 })
100}
101
102// NewTSSLSocketFromConnConf creates a TSSLSocket from an existing net.Conn.
103func NewTSSLSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSSLSocket {
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -0700104 return &TSSLSocket{
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800105 conn: wrapSocketConn(conn),
106 addr: conn.RemoteAddr(),
107 cfg: conf,
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -0700108 }
109}
110
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800111// Deprecated: Use NewTSSLSocketFromConnConf instead.
112func NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, socketTimeout time.Duration) *TSSLSocket {
113 return NewTSSLSocketFromConnConf(conn, &TConfiguration{
114 SocketTimeout: socketTimeout,
115 TLSConfig: cfg,
116
117 noPropagation: true,
118 })
119}
120
121// SetTConfiguration implements TConfigurationSetter.
122//
123// It can be used to change connect and socket timeouts.
124func (p *TSSLSocket) SetTConfiguration(conf *TConfiguration) {
125 p.cfg = conf
126}
127
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -0700128// Sets the connect timeout
129func (p *TSSLSocket) SetConnTimeout(timeout time.Duration) error {
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800130 if p.cfg == nil {
131 p.cfg = &TConfiguration{}
132 }
133 p.cfg.ConnectTimeout = timeout
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -0700134 return nil
Jens Geyer4c835952013-08-13 21:34:17 +0200135}
136
137// Sets the socket timeout
Yuxuan 'fishy' Wang5dc1d262020-06-27 08:34:31 -0700138func (p *TSSLSocket) SetSocketTimeout(timeout time.Duration) error {
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800139 if p.cfg == nil {
140 p.cfg = &TConfiguration{}
141 }
142 p.cfg.SocketTimeout = timeout
Jens Geyer4c835952013-08-13 21:34:17 +0200143 return nil
144}
145
146func (p *TSSLSocket) pushDeadline(read, write bool) {
147 var t time.Time
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800148 if timeout := p.cfg.GetSocketTimeout(); timeout > 0 {
149 t = time.Now().Add(time.Duration(timeout))
Jens Geyer4c835952013-08-13 21:34:17 +0200150 }
151 if read && write {
152 p.conn.SetDeadline(t)
153 } else if read {
154 p.conn.SetReadDeadline(t)
155 } else if write {
156 p.conn.SetWriteDeadline(t)
157 }
158}
159
160// Connects the socket, creating a new socket object if necessary.
161func (p *TSSLSocket) Open() error {
Jens Geyer4c835952013-08-13 21:34:17 +0200162 var err error
Jens Geyer00a4e3e2015-02-27 23:06:07 +0100163 // If we have a hostname, we need to pass the hostname to tls.Dial for
164 // certificate hostname checks.
165 if p.hostPort != "" {
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700166 if p.conn, err = createSocketConnFromReturn(tls.DialWithDialer(
167 &net.Dialer{
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800168 Timeout: p.cfg.GetConnectTimeout(),
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700169 },
170 "tcp",
171 p.hostPort,
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800172 p.cfg.GetTLSConfig(),
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700173 )); err != nil {
Yuxuan 'fishy' Wangfe3f8a12021-04-27 19:56:58 -0700174 return &tTransportException{
175 typeId: NOT_OPEN,
176 err: err,
177 msg: err.Error(),
178 }
Jens Geyer00a4e3e2015-02-27 23:06:07 +0100179 }
180 } else {
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700181 if p.conn.isValid() {
Jens Geyer00a4e3e2015-02-27 23:06:07 +0100182 return NewTTransportException(ALREADY_OPEN, "Socket already connected.")
183 }
184 if p.addr == nil {
185 return NewTTransportException(NOT_OPEN, "Cannot open nil address.")
186 }
187 if len(p.addr.Network()) == 0 {
188 return NewTTransportException(NOT_OPEN, "Cannot open bad network name.")
189 }
190 if len(p.addr.String()) == 0 {
191 return NewTTransportException(NOT_OPEN, "Cannot open bad address.")
192 }
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700193 if p.conn, err = createSocketConnFromReturn(tls.DialWithDialer(
194 &net.Dialer{
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800195 Timeout: p.cfg.GetConnectTimeout(),
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700196 },
197 p.addr.Network(),
198 p.addr.String(),
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800199 p.cfg.GetTLSConfig(),
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700200 )); err != nil {
Yuxuan 'fishy' Wangfe3f8a12021-04-27 19:56:58 -0700201 return &tTransportException{
202 typeId: NOT_OPEN,
203 err: err,
204 msg: err.Error(),
205 }
Jens Geyer00a4e3e2015-02-27 23:06:07 +0100206 }
Jens Geyer4c835952013-08-13 21:34:17 +0200207 }
208 return nil
209}
210
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +0100211// Retrieve the underlying net.Conn
Jens Geyer4c835952013-08-13 21:34:17 +0200212func (p *TSSLSocket) Conn() net.Conn {
213 return p.conn
214}
215
216// Returns true if the connection is open
217func (p *TSSLSocket) IsOpen() bool {
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700218 return p.conn.IsOpen()
Jens Geyer4c835952013-08-13 21:34:17 +0200219}
220
221// Closes the socket.
222func (p *TSSLSocket) Close() error {
Yuxuan 'fishy' Wang39d72782022-01-08 01:03:57 -0800223 return p.conn.Close()
Jens Geyer4c835952013-08-13 21:34:17 +0200224}
225
226func (p *TSSLSocket) Read(buf []byte) (int, error) {
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700227 if !p.conn.isValid() {
Jens Geyer4c835952013-08-13 21:34:17 +0200228 return 0, NewTTransportException(NOT_OPEN, "Connection not open")
229 }
230 p.pushDeadline(true, false)
Yuxuan 'fishy' Wangcfbb9052020-06-09 13:07:38 -0700231 // NOTE: Calling any of p.IsOpen, p.conn.read0, or p.conn.IsOpen between
232 // p.pushDeadline and p.conn.Read could cause the deadline set inside
233 // p.pushDeadline being reset, thus need to be avoided.
Jens Geyer4c835952013-08-13 21:34:17 +0200234 n, err := p.conn.Read(buf)
235 return n, NewTTransportExceptionFromError(err)
236}
237
238func (p *TSSLSocket) Write(buf []byte) (int, error) {
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700239 if !p.conn.isValid() {
Jens Geyer4c835952013-08-13 21:34:17 +0200240 return 0, NewTTransportException(NOT_OPEN, "Connection not open")
241 }
242 p.pushDeadline(false, true)
243 return p.conn.Write(buf)
244}
245
John Boiles57852792018-01-05 14:37:05 -0800246func (p *TSSLSocket) Flush(ctx context.Context) error {
Jens Geyer4c835952013-08-13 21:34:17 +0200247 return nil
248}
249
250func (p *TSSLSocket) Interrupt() error {
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700251 if !p.conn.isValid() {
Jens Geyer4c835952013-08-13 21:34:17 +0200252 return nil
253 }
254 return p.conn.Close()
255}
Jens Geyerca8469e2015-07-26 01:25:23 +0200256
257func (p *TSSLSocket) RemainingBytes() (num_bytes uint64) {
258 const maxSize = ^uint64(0)
Kevin Wojniakbc754672019-11-02 21:13:34 -0700259 return maxSize // the truth is, we just don't know unless framed is used
Jens Geyerca8469e2015-07-26 01:25:23 +0200260}
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800261
262var _ TConfigurationSetter = (*TSSLSocket)(nil)