How to use RemoteAddr method of conn Package

Best Gauge code snippet using conn.RemoteAddr

socks.go

Source:socks.go Github

copy

Full Screen

...125 conn = tls.Server(conn, selector.TLSConfig)126 }127 req, err := gosocks5.ReadUserPassRequest(conn)128 if err != nil {129 log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err)130 return nil, err131 }132 if Debug {133 log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), req.String())134 }135 valid := false136 for _, user := range selector.Users {137 username := user.Username()138 password, _ := user.Password()139 if (req.Username == username && req.Password == password) ||140 (req.Username == username && password == "") ||141 (username == "" && req.Password == password) {142 valid = true143 break144 }145 }146 if len(selector.Users) > 0 && !valid {147 resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)148 if err := resp.Write(conn); err != nil {149 log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err)150 return nil, err151 }152 if Debug {153 log.Log("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), resp)154 }155 log.Logf("[socks5] %s - %s: proxy authentication required", conn.RemoteAddr(), conn.LocalAddr())156 return nil, gosocks5.ErrAuthFailure157 }158 resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)159 if err := resp.Write(conn); err != nil {160 log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err)161 return nil, err162 }163 if Debug {164 log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), resp)165 }166 case gosocks5.MethodNoAcceptable:167 return nil, gosocks5.ErrBadMethod168 }169 return conn, nil170}171type socks5Connector struct {172 User *url.Userinfo173}174// SOCKS5Connector creates a connector for SOCKS5 proxy client.175// It accepts an optional auth info for SOCKS5 Username/Password Authentication.176func SOCKS5Connector(user *url.Userinfo) Connector {177 return &socks5Connector{User: user}178}179func (c *socks5Connector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) {180 selector := &clientSelector{181 TLSConfig: &tls.Config{InsecureSkipVerify: true},182 User: c.User,183 }184 selector.AddMethod(185 gosocks5.MethodNoAuth,186 gosocks5.MethodUserPass,187 MethodTLS,188 )189 cc := gosocks5.ClientConn(conn, selector)190 if err := cc.Handleshake(); err != nil {191 return nil, err192 }193 conn = cc194 host, port, err := net.SplitHostPort(addr)195 if err != nil {196 return nil, err197 }198 p, _ := strconv.Atoi(port)199 req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{200 Type: gosocks5.AddrDomain,201 Host: host,202 Port: uint16(p),203 })204 if err := req.Write(conn); err != nil {205 return nil, err206 }207 if Debug {208 log.Log("[socks5]", req)209 }210 reply, err := gosocks5.ReadReply(conn)211 if err != nil {212 return nil, err213 }214 if Debug {215 log.Log("[socks5]", reply)216 }217 if reply.Rep != gosocks5.Succeeded {218 return nil, errors.New("Service unavailable")219 }220 return conn, nil221}222type socks4Connector struct{}223// SOCKS4Connector creates a Connector for SOCKS4 proxy client.224func SOCKS4Connector() Connector {225 return &socks4Connector{}226}227func (c *socks4Connector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) {228 taddr, err := net.ResolveTCPAddr("tcp4", addr)229 if err != nil {230 return nil, err231 }232 if len(taddr.IP) == 0 {233 taddr.IP = net.IPv4zero234 }235 req := gosocks4.NewRequest(gosocks4.CmdConnect,236 &gosocks4.Addr{237 Type: gosocks4.AddrIPv4,238 Host: taddr.IP.String(),239 Port: uint16(taddr.Port),240 }, nil,241 )242 if err := req.Write(conn); err != nil {243 return nil, err244 }245 if Debug {246 log.Logf("[socks4] %s", req)247 }248 reply, err := gosocks4.ReadReply(conn)249 if err != nil {250 return nil, err251 }252 if Debug {253 log.Logf("[socks4] %s", reply)254 }255 if reply.Code != gosocks4.Granted {256 return nil, fmt.Errorf("[socks4] %d", reply.Code)257 }258 return conn, nil259}260type socks4aConnector struct{}261// SOCKS4AConnector creates a Connector for SOCKS4A proxy client.262func SOCKS4AConnector() Connector {263 return &socks4aConnector{}264}265func (c *socks4aConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) {266 host, port, err := net.SplitHostPort(addr)267 if err != nil {268 return nil, err269 }270 p, _ := strconv.Atoi(port)271 req := gosocks4.NewRequest(gosocks4.CmdConnect,272 &gosocks4.Addr{Type: gosocks4.AddrDomain, Host: host, Port: uint16(p)}, nil)273 if err := req.Write(conn); err != nil {274 return nil, err275 }276 if Debug {277 log.Logf("[socks4a] %s", req)278 }279 reply, err := gosocks4.ReadReply(conn)280 if err != nil {281 return nil, err282 }283 if Debug {284 log.Logf("[socks4a] %s", reply)285 }286 if reply.Code != gosocks4.Granted {287 return nil, fmt.Errorf("[socks4a] %d", reply.Code)288 }289 return conn, nil290}291type socks5Handler struct {292 selector *serverSelector293 options *HandlerOptions294}295// SOCKS5Handler creates a server Handler for SOCKS5 proxy server.296func SOCKS5Handler(opts ...HandlerOption) Handler {297 h := &socks5Handler{}298 h.Init(opts...)299 return h300}301func (h *socks5Handler) Init(options ...HandlerOption) {302 if h.options == nil {303 h.options = &HandlerOptions{}304 }305 for _, opt := range options {306 opt(h.options)307 }308 tlsConfig := h.options.TLSConfig309 if tlsConfig == nil {310 tlsConfig = DefaultTLSConfig311 }312 h.selector = &serverSelector{ // socks5 server selector313 Users: h.options.Users,314 TLSConfig: tlsConfig,315 }316 // methods that socks5 server supported317 h.selector.AddMethod(318 gosocks5.MethodNoAuth,319 gosocks5.MethodUserPass,320 MethodTLS,321 MethodTLSAuth,322 )323}324func (h *socks5Handler) Handle(conn net.Conn) {325 defer conn.Close()326 conn = gosocks5.ServerConn(conn, h.selector)327 req, err := gosocks5.ReadRequest(conn)328 if err != nil {329 log.Log("[socks5]", err)330 return331 }332 if Debug {333 log.Logf("[socks5] %s - %s\n%s", conn.RemoteAddr(), req.Addr, req)334 }335 switch req.Cmd {336 case gosocks5.CmdConnect:337 h.handleConnect(conn, req)338 case gosocks5.CmdBind:339 h.handleBind(conn, req)340 case gosocks5.CmdUdp:341 h.handleUDPRelay(conn, req)342 case CmdMuxBind:343 h.handleMuxBind(conn, req)344 case CmdUDPTun:345 h.handleUDPTunnel(conn, req)346 default:347 log.Log("[socks5] Unrecognized request:", req.Cmd)348 }349}350func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) {351 addr := req.Addr.String()352 if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) {353 log.Logf("[socks5-connect] Unauthorized to tcp connect to %s", addr)354 rep := gosocks5.NewReply(gosocks5.NotAllowed, nil)355 rep.Write(conn)356 if Debug {357 log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)358 }359 return360 }361 if h.options.Bypass.Contains(addr) {362 log.Logf("[socks5-connect] [bypass] %s", addr)363 rep := gosocks5.NewReply(gosocks5.NotAllowed, nil)364 rep.Write(conn)365 if Debug {366 log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)367 }368 return369 }370 cc, err := h.options.Chain.Dial(addr,371 RetryChainOption(h.options.Retries),372 TimeoutChainOption(h.options.Timeout),373 HostsChainOption(h.options.Hosts),374 ResolverChainOption(h.options.Resolver),375 )376 if err != nil {377 log.Logf("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)378 rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)379 rep.Write(conn)380 if Debug {381 log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)382 }383 return384 }385 defer cc.Close()386 rep := gosocks5.NewReply(gosocks5.Succeeded, nil)387 if err := rep.Write(conn); err != nil {388 log.Logf("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)389 return390 }391 if Debug {392 log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)393 }394 log.Logf("[socks5-connect] %s <-> %s", conn.RemoteAddr(), req.Addr)395 transport(conn, cc)396 log.Logf("[socks5-connect] %s >-< %s", conn.RemoteAddr(), req.Addr)397}398func (h *socks5Handler) handleBind(conn net.Conn, req *gosocks5.Request) {399 if h.options.Chain.IsEmpty() {400 addr := req.Addr.String()401 if !Can("rtcp", addr, h.options.Whitelist, h.options.Blacklist) {402 log.Logf("Unauthorized to tcp bind to %s", addr)403 return404 }405 h.bindOn(conn, addr)406 return407 }408 cc, err := h.options.Chain.Conn()409 if err != nil {410 log.Logf("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)411 reply := gosocks5.NewReply(gosocks5.Failure, nil)412 reply.Write(conn)413 if Debug {414 log.Logf("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply)415 }416 return417 }418 // forward request419 // note: this type of request forwarding is defined when starting server,420 // so we don't need to authenticate it, as it's as explicit as whitelisting421 defer cc.Close()422 req.Write(cc)423 log.Logf("[socks5-bind] %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr())424 transport(conn, cc)425 log.Logf("[socks5-bind] %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr())426}427func (h *socks5Handler) bindOn(conn net.Conn, addr string) {428 bindAddr, _ := net.ResolveTCPAddr("tcp", addr)429 ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error430 if err != nil {431 log.Logf("[socks5-bind] %s -> %s : %s", conn.RemoteAddr(), addr, err)432 gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)433 return434 }435 socksAddr := toSocksAddr(ln.Addr())436 // Issue: may not reachable when host has multi-interface437 socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())438 reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)439 if err := reply.Write(conn); err != nil {440 log.Logf("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), addr, err)441 ln.Close()442 return443 }444 if Debug {445 log.Logf("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), addr, reply)446 }447 log.Logf("[socks5-bind] %s - %s BIND ON %s OK", conn.RemoteAddr(), addr, socksAddr)448 var pconn net.Conn449 accept := func() <-chan error {450 errc := make(chan error, 1)451 go func() {452 defer close(errc)453 defer ln.Close()454 c, err := ln.AcceptTCP()455 if err != nil {456 errc <- err457 return458 }459 pconn = c460 }()461 return errc462 }463 pc1, pc2 := net.Pipe()464 pipe := func() <-chan error {465 errc := make(chan error, 1)466 go func() {467 defer close(errc)468 defer pc1.Close()469 errc <- transport(conn, pc1)470 }()471 return errc472 }473 defer pc2.Close()474 for {475 select {476 case err := <-accept():477 if err != nil || pconn == nil {478 log.Logf("[socks5-bind] %s <- %s : %v", conn.RemoteAddr(), addr, err)479 return480 }481 defer pconn.Close()482 reply := gosocks5.NewReply(gosocks5.Succeeded, toSocksAddr(pconn.RemoteAddr()))483 if err := reply.Write(pc2); err != nil {484 log.Logf("[socks5-bind] %s <- %s : %v", conn.RemoteAddr(), addr, err)485 }486 if Debug {487 log.Logf("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), addr, reply)488 }489 log.Logf("[socks5-bind] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), socksAddr, pconn.RemoteAddr())490 log.Logf("[socks5-bind] %s <-> %s", conn.RemoteAddr(), pconn.RemoteAddr())491 if err = transport(pc2, pconn); err != nil {492 log.Logf("[socks5-bind] %s - %s : %v", conn.RemoteAddr(), pconn.RemoteAddr(), err)493 }494 log.Logf("[socks5-bind] %s >-< %s", conn.RemoteAddr(), pconn.RemoteAddr())495 return496 case err := <-pipe():497 if err != nil {498 log.Logf("[socks5-bind] %s -> %s : %v", conn.RemoteAddr(), addr, err)499 }500 ln.Close()501 return502 }503 }504}505func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {506 addr := req.Addr.String()507 if !Can("udp", addr, h.options.Whitelist, h.options.Blacklist) {508 log.Logf("[socks5-udp] Unauthorized to udp connect to %s", addr)509 rep := gosocks5.NewReply(gosocks5.NotAllowed, nil)510 rep.Write(conn)511 if Debug {512 log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)513 }514 return515 }516 relay, err := net.ListenUDP("udp", nil)517 if err != nil {518 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), relay.LocalAddr(), err)519 reply := gosocks5.NewReply(gosocks5.Failure, nil)520 reply.Write(conn)521 if Debug {522 log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), relay.LocalAddr(), reply)523 }524 return525 }526 defer relay.Close()527 socksAddr := toSocksAddr(relay.LocalAddr())528 socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) // replace the IP to the out-going interface's529 reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)530 if err := reply.Write(conn); err != nil {531 log.Logf("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), relay.LocalAddr(), err)532 return533 }534 if Debug {535 log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), reply.Addr, reply)536 }537 log.Logf("[socks5-udp] %s - %s BIND ON %s OK", conn.RemoteAddr(), relay.LocalAddr(), socksAddr)538 // serve as standard socks5 udp relay local <-> remote539 if h.options.Chain.IsEmpty() {540 peer, er := net.ListenUDP("udp", nil)541 if er != nil {542 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), socksAddr, er)543 return544 }545 defer peer.Close()546 go h.transportUDP(relay, peer)547 log.Logf("[socks5-udp] %s <-> %s", conn.RemoteAddr(), socksAddr)548 if err := h.discardClientData(conn); err != nil {549 log.Logf("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), socksAddr, err)550 }551 log.Logf("[socks5-udp] %s >-< %s", conn.RemoteAddr(), socksAddr)552 return553 }554 cc, err := h.options.Chain.Conn()555 // connection error556 if err != nil {557 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), socksAddr, err)558 return559 }560 // forward udp local <-> tunnel561 defer cc.Close()562 cc, err = socks5Handshake(cc, h.options.Chain.LastNode().User)563 if err != nil {564 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), socksAddr, err)565 return566 }567 cc.SetWriteDeadline(time.Now().Add(WriteTimeout))568 r := gosocks5.NewRequest(CmdUDPTun, nil)569 if err := r.Write(cc); err != nil {570 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), cc.RemoteAddr(), err)571 return572 }573 cc.SetWriteDeadline(time.Time{})574 if Debug {575 log.Logf("[socks5-udp] %s -> %s\n%s", conn.RemoteAddr(), cc.RemoteAddr(), r)576 }577 cc.SetReadDeadline(time.Now().Add(ReadTimeout))578 reply, err = gosocks5.ReadReply(cc)579 if err != nil {580 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), cc.RemoteAddr(), err)581 return582 }583 if Debug {584 log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), cc.RemoteAddr(), reply)585 }586 if reply.Rep != gosocks5.Succeeded {587 log.Logf("[socks5-udp] %s <- %s : udp associate failed", conn.RemoteAddr(), cc.RemoteAddr())588 return589 }590 cc.SetReadDeadline(time.Time{})591 log.Logf("[socks5-udp] %s <-> %s [tun: %s]", conn.RemoteAddr(), socksAddr, reply.Addr)592 go h.tunnelClientUDP(relay, cc)593 log.Logf("[socks5-udp] %s <-> %s", conn.RemoteAddr(), socksAddr)594 if err := h.discardClientData(conn); err != nil {595 log.Logf("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), socksAddr, err)596 }597 log.Logf("[socks5-udp] %s >-< %s", conn.RemoteAddr(), socksAddr)598}599func (h *socks5Handler) discardClientData(conn net.Conn) (err error) {600 b := make([]byte, tinyBufferSize)601 n := 0602 for {603 n, err = conn.Read(b) // discard any data from tcp connection604 if err != nil {605 if err == io.EOF { // disconnect normally606 err = nil607 }608 break // client disconnected609 }610 log.Logf("[socks5-udp] read %d UNEXPECTED TCP data from client", n)611 }612 return613}614func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {615 errc := make(chan error, 2)616 var clientAddr *net.UDPAddr617 go func() {618 b := make([]byte, largeBufferSize)619 for {620 n, laddr, err := relay.ReadFromUDP(b)621 if err != nil {622 errc <- err623 return624 }625 if clientAddr == nil {626 clientAddr = laddr627 }628 dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))629 if err != nil {630 errc <- err631 return632 }633 raddr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())634 if err != nil {635 continue // drop silently636 }637 if h.options.Bypass.Contains(raddr.String()) {638 log.Log("[socks5-udp] [bypass] write to", raddr)639 continue // bypass640 }641 if _, err := peer.WriteToUDP(dgram.Data, raddr); err != nil {642 errc <- err643 return644 }645 if Debug {646 log.Logf("[socks5-udp] %s >>> %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data))647 }648 }649 }()650 go func() {651 b := make([]byte, largeBufferSize)652 for {653 n, raddr, err := peer.ReadFromUDP(b)654 if err != nil {655 errc <- err656 return657 }658 if clientAddr == nil {659 continue660 }661 if h.options.Bypass.Contains(raddr.String()) {662 log.Log("[socks5-udp] [bypass] read from", raddr)663 continue // bypass664 }665 buf := bytes.Buffer{}666 dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), b[:n])667 dgram.Write(&buf)668 if _, err := relay.WriteToUDP(buf.Bytes(), clientAddr); err != nil {669 errc <- err670 return671 }672 if Debug {673 log.Logf("[socks5-udp] %s <<< %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data))674 }675 }676 }()677 select {678 case err = <-errc:679 //log.Println("w exit", err)680 }681 return682}683func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error) {684 errc := make(chan error, 2)685 var clientAddr *net.UDPAddr686 go func() {687 b := make([]byte, mediumBufferSize)688 for {689 n, addr, err := uc.ReadFromUDP(b)690 if err != nil {691 log.Logf("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), addr, err)692 errc <- err693 return694 }695 // glog.V(LDEBUG).Infof("read udp %d, % #x", n, b[:n])696 // pipe from relay to tunnel697 dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))698 if err != nil {699 errc <- err700 return701 }702 if clientAddr == nil {703 clientAddr = addr704 }705 raddr := dgram.Header.Addr.String()706 if h.options.Bypass.Contains(raddr) {707 log.Log("[udp-tun] [bypass] write to", raddr)708 continue // bypass709 }710 dgram.Header.Rsv = uint16(len(dgram.Data))711 if err := dgram.Write(cc); err != nil {712 errc <- err713 return714 }715 if Debug {716 log.Logf("[udp-tun] %s >>> %s length: %d", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data))717 }718 }719 }()720 go func() {721 for {722 dgram, err := gosocks5.ReadUDPDatagram(cc)723 if err != nil {724 log.Logf("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err)725 errc <- err726 return727 }728 // pipe from tunnel to relay729 if clientAddr == nil {730 continue731 }732 raddr := dgram.Header.Addr.String()733 if h.options.Bypass.Contains(raddr) {734 log.Log("[udp-tun] [bypass] read from", raddr)735 continue // bypass736 }737 dgram.Header.Rsv = 0738 buf := bytes.Buffer{}739 dgram.Write(&buf)740 if _, err := uc.WriteToUDP(buf.Bytes(), clientAddr); err != nil {741 errc <- err742 return743 }744 if Debug {745 log.Logf("[udp-tun] %s <<< %s length: %d", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data))746 }747 }748 }()749 select {750 case err = <-errc:751 }752 return753}754func (h *socks5Handler) handleUDPTunnel(conn net.Conn, req *gosocks5.Request) {755 // serve tunnel udp, tunnel <-> remote, handle tunnel udp request756 if h.options.Chain.IsEmpty() {757 addr := req.Addr.String()758 if !Can("rudp", addr, h.options.Whitelist, h.options.Blacklist) {759 log.Logf("[socks5-udp] Unauthorized to udp bind to %s", addr)760 return761 }762 bindAddr, _ := net.ResolveUDPAddr("udp", addr)763 uc, err := net.ListenUDP("udp", bindAddr)764 if err != nil {765 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)766 return767 }768 defer uc.Close()769 socksAddr := toSocksAddr(uc.LocalAddr())770 socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())771 reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)772 if err := reply.Write(conn); err != nil {773 log.Logf("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), socksAddr, err)774 return775 }776 if Debug {777 log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), socksAddr, reply)778 }779 log.Logf("[socks5-udp] %s <-> %s", conn.RemoteAddr(), socksAddr)780 h.tunnelServerUDP(conn, uc)781 log.Logf("[socks5-udp] %s >-< %s", conn.RemoteAddr(), socksAddr)782 return783 }784 cc, err := h.options.Chain.Conn()785 // connection error786 if err != nil {787 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)788 reply := gosocks5.NewReply(gosocks5.Failure, nil)789 reply.Write(conn)790 log.Logf("[socks5-udp] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, reply)791 return792 }793 defer cc.Close()794 cc, err = socks5Handshake(cc, h.options.Chain.LastNode().User)795 if err != nil {796 log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)797 return798 }799 // tunnel <-> tunnel, direct forwarding800 // note: this type of request forwarding is defined when starting server801 // so we don't need to authenticate it, as it's as explicit as whitelisting802 req.Write(cc)803 log.Logf("[socks5-udp] %s <-> %s [tun]", conn.RemoteAddr(), cc.RemoteAddr())804 transport(conn, cc)805 log.Logf("[socks5-udp] %s >-< %s [tun]", conn.RemoteAddr(), cc.RemoteAddr())806}807func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error) {808 errc := make(chan error, 2)809 go func() {810 b := make([]byte, mediumBufferSize)811 for {812 n, addr, err := uc.ReadFromUDP(b)813 if err != nil {814 log.Logf("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), addr, err)815 errc <- err816 return817 }818 if h.options.Bypass.Contains(addr.String()) {819 log.Log("[udp-tun] [bypass] read from", addr)820 continue // bypass821 }822 // pipe from peer to tunnel823 dgram := gosocks5.NewUDPDatagram(824 gosocks5.NewUDPHeader(uint16(n), 0, toSocksAddr(addr)), b[:n])825 if err := dgram.Write(cc); err != nil {826 log.Logf("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), dgram.Header.Addr, err)827 errc <- err828 return829 }830 if Debug {831 log.Logf("[udp-tun] %s <<< %s length: %d", cc.RemoteAddr(), dgram.Header.Addr, len(dgram.Data))832 }833 }834 }()835 go func() {836 for {837 dgram, err := gosocks5.ReadUDPDatagram(cc)838 if err != nil {839 log.Logf("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err)840 errc <- err841 return842 }843 // pipe from tunnel to peer844 addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())845 if err != nil {846 continue // drop silently847 }848 if h.options.Bypass.Contains(addr.String()) {849 log.Log("[udp-tun] [bypass] write to", addr)850 continue // bypass851 }852 if _, err := uc.WriteToUDP(dgram.Data, addr); err != nil {853 log.Logf("[udp-tun] %s -> %s : %s", cc.RemoteAddr(), addr, err)854 errc <- err855 return856 }857 if Debug {858 log.Logf("[udp-tun] %s >>> %s length: %d", cc.RemoteAddr(), addr, len(dgram.Data))859 }860 }861 }()862 select {863 case err = <-errc:864 }865 return866}867func (h *socks5Handler) handleMuxBind(conn net.Conn, req *gosocks5.Request) {868 if h.options.Chain.IsEmpty() {869 addr := req.Addr.String()870 if !Can("rtcp", addr, h.options.Whitelist, h.options.Blacklist) {871 log.Logf("Unauthorized to tcp mbind to %s", addr)872 return873 }874 h.muxBindOn(conn, addr)875 return876 }877 cc, err := h.options.Chain.Conn()878 if err != nil {879 log.Logf("[socks5-mbind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)880 reply := gosocks5.NewReply(gosocks5.Failure, nil)881 reply.Write(conn)882 if Debug {883 log.Logf("[socks5-mbind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply)884 }885 return886 }887 // forward request888 // note: this type of request forwarding is defined when starting server,889 // so we don't need to authenticate it, as it's as explicit as whitelisting.890 defer cc.Close()891 req.Write(cc)892 log.Logf("[socks5-mbind] %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr())893 transport(conn, cc)894 log.Logf("[socks5-mbind] %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr())895}896func (h *socks5Handler) muxBindOn(conn net.Conn, addr string) {897 bindAddr, _ := net.ResolveTCPAddr("tcp", addr)898 ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error899 if err != nil {900 log.Logf("[socks5-mbind] %s -> %s : %s", conn.RemoteAddr(), addr, err)901 gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)902 return903 }904 defer ln.Close()905 socksAddr := toSocksAddr(ln.Addr())906 // Issue: may not reachable when host has multi-interface.907 socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())908 reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)909 if err := reply.Write(conn); err != nil {910 log.Logf("[socks5-mbind] %s <- %s : %s", conn.RemoteAddr(), addr, err)911 return912 }913 if Debug {914 log.Logf("[socks5-mbind] %s <- %s\n%s", conn.RemoteAddr(), addr, reply)915 }916 log.Logf("[socks5-mbind] %s - %s BIND ON %s OK", conn.RemoteAddr(), addr, socksAddr)917 // Upgrade connection to multiplex stream.918 s, err := smux.Client(conn, smux.DefaultConfig())919 if err != nil {920 log.Logf("[socks5-mbind] %s - %s : %s", conn.RemoteAddr(), socksAddr, err)921 return922 }923 log.Logf("[socks5-mbind] %s <-> %s", conn.RemoteAddr(), socksAddr)924 defer log.Logf("[socks5-mbind] %s >-< %s", conn.RemoteAddr(), socksAddr)925 session := &muxSession{926 conn: conn,927 session: s,928 }929 defer session.Close()930 go func() {931 for {932 conn, err := session.Accept()933 if err != nil {934 ln.Close()935 return936 }937 conn.Close() // we do not handle incoming connection.938 }939 }()940 for {941 cc, err := ln.Accept()942 if err != nil {943 // log.Logf("[socks5-mbind] %s <- %s : %v", conn.RemoteAddr(), socksAddr, err)944 return945 }946 log.Logf("[socks5-mbind] %s <- %s : ACCEPT peer %s",947 conn.RemoteAddr(), socksAddr, cc.RemoteAddr())948 go func(c net.Conn) {949 defer c.Close()950 sc, err := session.GetConn()951 if err != nil {952 log.Logf("[socks5-mbind] %s <- %s : %s", conn.RemoteAddr(), socksAddr, err)953 return954 }955 transport(sc, c)956 }(cc)957 }958}959func toSocksAddr(addr net.Addr) *gosocks5.Addr {960 host := "0.0.0.0"961 port := 0962 if addr != nil {963 h, p, _ := net.SplitHostPort(addr.String())964 host = h965 port, _ = strconv.Atoi(p)966 }967 return &gosocks5.Addr{968 Type: gosocks5.AddrIPv4,969 Host: host,970 Port: uint16(port),971 }972}973type socks4Handler struct {974 options *HandlerOptions975}976// SOCKS4Handler creates a server Handler for SOCKS4(A) proxy server.977func SOCKS4Handler(opts ...HandlerOption) Handler {978 h := &socks4Handler{}979 h.Init(opts...)980 return h981}982func (h *socks4Handler) Init(options ...HandlerOption) {983 if h.options == nil {984 h.options = &HandlerOptions{}985 }986 for _, opt := range options {987 opt(h.options)988 }989}990func (h *socks4Handler) Handle(conn net.Conn) {991 defer conn.Close()992 req, err := gosocks4.ReadRequest(conn)993 if err != nil {994 log.Log("[socks4]", err)995 return996 }997 if Debug {998 log.Logf("[socks4] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req)999 }1000 switch req.Cmd {1001 case gosocks4.CmdConnect:1002 log.Logf("[socks4-connect] %s -> %s", conn.RemoteAddr(), req.Addr)1003 h.handleConnect(conn, req)1004 case gosocks4.CmdBind:1005 log.Logf("[socks4-bind] %s - %s", conn.RemoteAddr(), req.Addr)1006 h.handleBind(conn, req)1007 default:1008 log.Logf("[socks4] Unrecognized request: %d", req.Cmd)1009 }1010}1011func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) {1012 addr := req.Addr.String()1013 if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) {1014 log.Logf("[socks4-connect] Unauthorized to tcp connect to %s", addr)1015 rep := gosocks4.NewReply(gosocks4.Rejected, nil)1016 rep.Write(conn)1017 if Debug {1018 log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)1019 }1020 return1021 }1022 if h.options.Bypass.Contains(addr) {1023 log.Log("[socks4-connect] [bypass]", addr)1024 rep := gosocks4.NewReply(gosocks4.Rejected, nil)1025 rep.Write(conn)1026 if Debug {1027 log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)1028 }1029 return1030 }1031 cc, err := h.options.Chain.Dial(addr,1032 RetryChainOption(h.options.Retries),1033 TimeoutChainOption(h.options.Timeout),1034 )1035 if err != nil {1036 log.Logf("[socks4-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)1037 rep := gosocks4.NewReply(gosocks4.Failed, nil)1038 rep.Write(conn)1039 if Debug {1040 log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)1041 }1042 return1043 }1044 defer cc.Close()1045 rep := gosocks4.NewReply(gosocks4.Granted, nil)1046 if err := rep.Write(conn); err != nil {1047 log.Logf("[socks4-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)1048 return1049 }1050 if Debug {1051 log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)1052 }1053 log.Logf("[socks4-connect] %s <-> %s", conn.RemoteAddr(), req.Addr)1054 transport(conn, cc)1055 log.Logf("[socks4-connect] %s >-< %s", conn.RemoteAddr(), req.Addr)1056}1057func (h *socks4Handler) handleBind(conn net.Conn, req *gosocks4.Request) {1058 // TODO: serve socks4 bind1059 if h.options.Chain.IsEmpty() {1060 reply := gosocks4.NewReply(gosocks4.Rejected, nil)1061 reply.Write(conn)1062 if Debug {1063 log.Logf("[socks4-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply)1064 }1065 return1066 }1067 cc, err := h.options.Chain.Conn()1068 // connection error1069 if err != nil && err != ErrEmptyChain {1070 log.Logf("[socks4-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)1071 reply := gosocks4.NewReply(gosocks4.Failed, nil)1072 reply.Write(conn)1073 if Debug {1074 log.Logf("[socks4-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply)1075 }1076 return1077 }1078 defer cc.Close()1079 // forward request1080 req.Write(cc)1081 log.Logf("[socks4-bind] %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr())1082 transport(conn, cc)1083 log.Logf("[socks4-bind] %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr())1084}1085func getSOCKS5UDPTunnel(chain *Chain, addr net.Addr) (net.Conn, error) {1086 conn, err := chain.Conn()1087 if err != nil {1088 return nil, err1089 }1090 cc, err := socks5Handshake(conn, chain.LastNode().User)1091 if err != nil {1092 conn.Close()1093 return nil, err1094 }1095 conn = cc1096 conn.SetWriteDeadline(time.Now().Add(WriteTimeout))1097 req := gosocks5.NewRequest(CmdUDPTun, toSocksAddr(addr))...

Full Screen

Full Screen

udp.go

Source:udp.go Github

copy

Full Screen

...23)24type UdpPacket struct {25 Buf []byte26 LocalAddr net.Addr27 RemoteAddr net.Addr28}29type FakeUdpConn struct {30 log.Logger31 l *UdpListener32 localAddr net.Addr33 remoteAddr net.Addr34 packets chan []byte35 closeFlag bool36 lastActive time.Time37 mu sync.RWMutex38}39func NewFakeUdpConn(l *UdpListener, laddr, raddr net.Addr) *FakeUdpConn {40 fc := &FakeUdpConn{41 Logger: log.NewPrefixLogger(""),42 l: l,43 localAddr: laddr,44 remoteAddr: raddr,45 packets: make(chan []byte, 20),46 }47 go func() {48 for {49 time.Sleep(5 * time.Second)50 fc.mu.RLock()51 if time.Now().Sub(fc.lastActive) > 10*time.Second {52 fc.mu.RUnlock()53 fc.Close()54 break55 }56 fc.mu.RUnlock()57 }58 }()59 return fc60}61func (c *FakeUdpConn) putPacket(content []byte) {62 defer func() {63 if err := recover(); err != nil {64 }65 }()66 select {67 case c.packets <- content:68 default:69 }70}71func (c *FakeUdpConn) Read(b []byte) (n int, err error) {72 content, ok := <-c.packets73 if !ok {74 return 0, io.EOF75 }76 c.mu.Lock()77 c.lastActive = time.Now()78 c.mu.Unlock()79 if len(b) < len(content) {80 n = len(b)81 } else {82 n = len(content)83 }84 copy(b, content)85 return n, nil86}87func (c *FakeUdpConn) Write(b []byte) (n int, err error) {88 c.mu.RLock()89 if c.closeFlag {90 c.mu.RUnlock()91 return 0, io.ErrClosedPipe92 }93 c.mu.RUnlock()94 packet := &UdpPacket{95 Buf: b,96 LocalAddr: c.localAddr,97 RemoteAddr: c.remoteAddr,98 }99 c.l.writeUdpPacket(packet)100 c.mu.Lock()101 c.lastActive = time.Now()102 c.mu.Unlock()103 return len(b), nil104}105func (c *FakeUdpConn) Close() error {106 c.mu.Lock()107 defer c.mu.Unlock()108 if !c.closeFlag {109 c.closeFlag = true110 close(c.packets)111 }112 return nil113}114func (c *FakeUdpConn) IsClosed() bool {115 c.mu.RLock()116 defer c.mu.RUnlock()117 return c.closeFlag118}119func (c *FakeUdpConn) LocalAddr() net.Addr {120 return c.localAddr121}122func (c *FakeUdpConn) RemoteAddr() net.Addr {123 return c.remoteAddr124}125func (c *FakeUdpConn) SetDeadline(t time.Time) error {126 return nil127}128func (c *FakeUdpConn) SetReadDeadline(t time.Time) error {129 return nil130}131func (c *FakeUdpConn) SetWriteDeadline(t time.Time) error {132 return nil133}134type UdpListener struct {135 net.Addr136 accept chan Conn137 writeCh chan *UdpPacket138 readConn net.Conn139 closeFlag bool140 fakeConns map[string]*FakeUdpConn141 log.Logger142}143func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) {144 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", bindAddr, bindPort))145 if err != nil {146 return l, err147 }148 readConn, err := net.ListenUDP("udp", udpAddr)149 l = &UdpListener{150 Addr: udpAddr,151 accept: make(chan Conn),152 writeCh: make(chan *UdpPacket, 1000),153 fakeConns: make(map[string]*FakeUdpConn),154 Logger: log.NewPrefixLogger(""),155 }156 // for reading157 go func() {158 for {159 buf := pool.GetBuf(1450)160 n, remoteAddr, err := readConn.ReadFromUDP(buf)161 if err != nil {162 close(l.accept)163 close(l.writeCh)164 return165 }166 fakeConn, exist := l.fakeConns[remoteAddr.String()]167 if !exist || fakeConn.IsClosed() {168 fakeConn = NewFakeUdpConn(l, l.Addr, remoteAddr)169 l.fakeConns[remoteAddr.String()] = fakeConn170 }171 fakeConn.putPacket(buf[:n])172 l.accept <- fakeConn173 }174 }()175 // for writing176 go func() {177 for {178 packet, ok := <-l.writeCh179 if !ok {180 return181 }182 if addr, ok := packet.RemoteAddr.(*net.UDPAddr); ok {183 readConn.WriteToUDP(packet.Buf, addr)184 }185 }186 }()187 return188}189func (l *UdpListener) writeUdpPacket(packet *UdpPacket) (err error) {190 defer func() {191 if errRet := recover(); errRet != nil {192 err = fmt.Errorf("udp write closed listener")193 l.Info("udp write closed listener")194 }195 }()196 l.writeCh <- packet197 return198}199func (l *UdpListener) WriteMsg(buf []byte, remoteAddr *net.UDPAddr) (err error) {200 // only set remote addr here201 packet := &UdpPacket{202 Buf: buf,203 RemoteAddr: remoteAddr,204 }205 err = l.writeUdpPacket(packet)206 return207}208func (l *UdpListener) Accept() (Conn, error) {209 conn, ok := <-l.accept210 if !ok {211 return conn, fmt.Errorf("channel for udp listener closed")212 }213 return conn, nil214}215func (l *UdpListener) Close() error {216 if !l.closeFlag {217 l.closeFlag = true...

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 conn, err := net.Dial("tcp", "golang.org:80")4 if err != nil {5 fmt.Println(err)6 }7 defer conn.Close()8 fmt.Println(conn.RemoteAddr())9}

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 if len(os.Args) != 2 {4 fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])5 os.Exit(1)6 }7 tcpAddr, err := net.ResolveTCPAddr("tcp4", service)8 checkError(err)9 conn, err := net.DialTCP("tcp", nil, tcpAddr)10 checkError(err)11 _, err = conn.Write([]byte("HEAD / HTTP/1.0\r12 checkError(err)13 result, err := readFully(conn)14 checkError(err)15 fmt.Println(string(result))16 os.Exit(0)17}18func readFully(conn net.Conn) ([]byte, error) {19 defer conn.Close()20 result := bytes.NewBuffer(nil)21 for {22 n, err := conn.Read(buf[0:])23 result.Write(buf[0:n])24 if err != nil {25 if err == io.EOF {26 }27 }28 }29 return result.Bytes(), nil30}31func checkError(err error) {32 if err != nil {33 fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())34 os.Exit(1)35 }36}

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 conn, err := net.Dial("tcp", "golang.org:80")4 if err != nil {5 }6 defer conn.Close()7 fmt.Println(conn.RemoteAddr())8}

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 if len(os.Args) != 2 {4 fmt.Fprintf(os.Stderr, "Usage: %s ip-addr", os.Args[0])5 os.Exit(1)6 }7 addr := net.ParseIP(name)8 if addr == nil {9 fmt.Println("Invalid address")10 } else {11 fmt.Println("The address is", addr.String())12 }13 os.Exit(0)14}

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3conn, err := net.Dial("tcp", "golang.org:80")4if err != nil {5}6defer conn.Close()7fmt.Println(conn.RemoteAddr())8}

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3conn, err := net.Dial("tcp", "google.com:80")4if err != nil {5fmt.Println(err)6}7defer conn.Close()8fmt.Println(conn.RemoteAddr())9}

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 conn, err := net.Dial("tcp", "google.com:80")4 if err != nil {5 fmt.Println("Error connecting to server")6 }7 defer conn.Close()8 fmt.Println("Connected to server")9 fmt.Println("Remote Address:", conn.RemoteAddr())10}

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 conn, _ := net.Dial("tcp", "www.google.com:80")4 fmt.Println(conn.RemoteAddr())5 conn.Close()6}

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 ln, err := net.Listen("tcp", ":8080")4 if err != nil {5 log.Fatal(err)6 }7 defer ln.Close()8 for {9 conn, err := ln.Accept()10 if err != nil {11 log.Println(err)12 }13 fmt.Println(conn.RemoteAddr())14 conn.Close()15 }16}17import (18func main() {19 ln, err := net.ListenUDP("udp", &net.UDPAddr{Port: 8080})20 if err != nil {21 log.Fatal(err)22 }23 defer ln.Close()24 for {25 b := make([]byte, 1500)26 n, addr, err := ln.ReadFromUDP(b)27 if err != nil {28 log.Println(err)29 }30 fmt.Println(addr)31 fmt.Println(string(b[:n]))32 }33}34import (35func main() {36 ln, err := net.ListenPacket("udp", ":8080")37 if err != nil {38 log.Fatal(err)39 }40 defer ln.Close()41 for {42 b := make([]byte, 1500)43 n, addr, err := ln.ReadFrom(b)44 if err != nil {45 log.Println(err)46 }47 fmt.Println(addr)48 fmt.Println(string(b[:n]))49 }50}51import (52func main() {53 ln, err := net.ListenPacket("udp", ":8080")54 if err != nil {55 log.Fatal(err)56 }57 defer ln.Close()58 for {59 b := make([]byte, 1500)60 n, addr, err := ln.ReadFrom(b)61 if err != nil {62 log.Println(err)63 }64 fmt.Println(addr

Full Screen

Full Screen

RemoteAddr

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 conn, err := net.Dial("tcp", "golang.org:80")4 if err != nil {5 fmt.Println(err)6 }7 defer conn.Close()8 fmt.Println(conn.RemoteAddr())9}

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful