Friday, April 29, 2016

Scope of sessionStorage and localStorage

Scope of sessionStorage and localStorage

Each key-name pair is unique for a protocol and domain, regardless of the paths.

The affected domain can be changed via the document.domain property.

sub.domain.com -> domain.com is possible (subdomain)
sub.domain.com -> other.domain.com is not possible

Session Storage:

Values persist only as long as the window or tab in which they stored.
Values are only visible within the window or tab that created them.

Local Storage:

Values persist window and browser lifetimes.
Values are shared across every window or tab running at the same origin.

So, by reading and understanding this each key-value pair is unique for each domain, because local storage persist values across window or tab.

  localStorage.setItem("test", "Hello World");

  var test = localStorage.getItem("test");
  console.log(test);

  for(var i in window.localStorage) {
    console.log(i + ": " + window.localStorage[i]);
  }

Reference:

http://stackoverflow.com/questions/9742395/scope-of-sessionstorage-and-localstorage

http://stackoverflow.com/questions/4201239/in-html5-is-the-localstorage-object-isolated-per-page-domain

Monday, April 25, 2016

Time format conversion in golang

Time format conversion in golang

# go run test_time.go 127.0.0.1

test_time.go:

package main

import (
        "flag"
        "fmt"
        "gopkg.in/redis.v3"
        "log"
        "reflect"
        "strconv"
        "time"
)

func main() {
        flag.Parse()

        if flag.NArg() != 1 {
                log.Fatal("Please specify Redis server IP")
        }

        redisServer := flag.Args()[0]

        client := redis.NewClient(&redis.Options{
                Addr:     redisServer + ":6379",
                Password: "", // no password set
                DB:       0,  // use default DB
        })

        t := time.Now()

        // returns t as a Unix time, the number of nanoseconds elapsed since January 1, 1970 UTC
        t1 := t.UnixNano()

        // returns the string representation of t1 in 16 base
        t2 := strconv.FormatInt(t1, 16)

        t3, _ := strconv.ParseInt(t2, 16, 64)

        t4 := time.Unix(0, t3)

        fmt.Printf("t1: %v, %v\n", t1, reflect.TypeOf(t1))
        fmt.Printf("t2: %v, %v\n", t2, reflect.TypeOf(t2))
        fmt.Printf("t3: %v, %v\n", t3, reflect.TypeOf(t3))
        fmt.Printf("t4: %v, %v\n", t4, reflect.TypeOf(t4))

        err := client.Set("MyLastMod", t1, 0).Err()

        if err != nil {
                fmt.Printf("ERR: %s\n", err)
        }

        s, err := client.Get("MyLastMod").Int64()

        client.Close()

        if err != nil {
                fmt.Printf("ERR: %s\n", err)
        }

        fmt.Printf("Last Modified Time: %v %v\n", s, reflect.TypeOf(s))
}

Sample output:

t1: 1461628876922842766, int64
t2: 1448bfb832706e8e, string
t3: 1461628876922842766, int64
t4: 2016-04-25 17:01:16.922842766 -0700 PDT, time.Time
Last Modified Time: 1461628876922842766 int64

Loop through golang struct

Loop through golang struct

package main

import (
  "fmt"
  "reflect"
)

type person struct {
  Status    bool
  Firstname string
  Lastname  string
  Age       int
}

func main() {
  typ := reflect.TypeOf(person {})

  fmt.Printf("%v\n", typ)

  for i := 0; i < typ.NumField(); i++ {
    fmt.Printf("%v %v\n", typ.Field(i).Name, typ.Field(i).Type)
  }
}

Sunday, April 24, 2016

Useful Go Library (some PHP, JavaScript)

Useful Go Library (some PHP, JavaScript)

Gorilla is a web toolkit for the Go programming language

https://github.com/gorilla/websocket
https://github.com/gorilla/csrf
https://github.com/gorilla/mux

Golang implementation of JSON Web Tokens (JWT)

https://github.com/dgrijalva/jwt-go

Redis client for Golang

https://github.com/go-redis/redis

Golang module for google re-captcha

https://github.com/haisum/recaptcha

Time-based One Time Passwords (TOTP) two factor authentication library

https://github.com/pquerna/otp

Golang two factor authentication library

https://github.com/sec51/twofactor

Google Authenticator for Go

https://github.com/dgryski/dgoogauth

Go implementation of RFC 4226 OATH-HOTP authentication

https://github.com/gokyle/hotp

A small decorator for the JavaScript WebSocket API that automatically reconnects (JavaScript)

https://github.com/joewalnes/reconnecting-websocket

A PHP extension for Redis (PHP)

https://github.com/phpredis/phpredis

Go development plugin for Vim

https://github.com/fatih/vim-go

Fast PostgreSQL client and ORM for Golang

https://github.com/go-pg/pg

example of using JSON Web Tokens JWT for http authentication in Go

example of using JSON Web Tokens JWT for http authentication in Go

Saturday, April 23, 2016

two factor authentication TOTP (RFC 6238) golang example

two factor authentication TOTP (RFC 6238) golang example

package main

import (
        "github.com/pquerna/otp"
        "github.com/pquerna/otp/totp"
        "gopkg.in/redis.v3"

        "bufio"
        "bytes"
        "fmt"
        "image/png"
        //"io/ioutil"
        "log"
        "net/http"
        "os"
        "strconv"
)

var redisClient = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
})

func display(key *otp.Key, data []byte) {
        fmt.Printf("Issuer:       %s\n", key.Issuer())
        fmt.Printf("Account Name: %s\n", key.AccountName())
        fmt.Printf("Secret:       %s\n", key.Secret())
        fmt.Println("Writing PNG to qr-code.png....")
        //ioutil.WriteFile("qr-code.png", data, 0644)
        fmt.Println("")
        fmt.Println("Please add your TOTP to your OTP Application now!")
        fmt.Println("")
}

func promptForPasscode() string {
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("Enter Passcode: ")
        text, _ := reader.ReadString('\n')
        return text
}

func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Home: %s!", r.URL.Path[1:])
}

func writeImage(w http.ResponseWriter, r *http.Request) {
        key, err := totp.Generate(totp.GenerateOpts{
                Issuer:      "Example.com",
                AccountName: "test@example.com",
        })

        if err != nil {
                panic(err)
        }

        // Convert TOTP key into a PNG
        var buf bytes.Buffer

        img, err := key.Image(200, 200)

        if err != nil {
                panic(err)
        }

        png.Encode(&buf, img)

        // display the QR code to the user.
        qrBytes := buf.Bytes()
        display(key, qrBytes)

        if err := redisClient.Set("test@example.com", key.Secret(), 0).Err(); err != nil {
                log.Printf("H: %v\n\n", err)
        }

        w.Header().Set("Content-Type", "image/png")
        w.Header().Set("Content-Length", strconv.Itoa(len(qrBytes)))

        if _, err := w.Write(qrBytes); err != nil {
                log.Println("unable to write image.")
        }
}

func verifyIt(w http.ResponseWriter, r *http.Request) {
        // Now Validate that the user's successfully added the passcode.
        fmt.Println("Validating TOTP...")
        //passcode := promptForPasscode()

        val, err := redisClient.Get("test@example.com").Result()

        if err != nil {
                log.Println("call Result error.")
        }

        valid := totp.Validate(r.URL.Path[3:], val)

        if valid {
                fmt.Fprintf(w, "Good Job: %s.\n", r.URL.Path[3:])
        } else {
                fmt.Fprintf(w, "Wrong code: %s.\n", r.URL.Path[3:])
        }
}

func main() {

        http.HandleFunc("/v/", verifyIt)
        http.HandleFunc("/qr/", writeImage)
        http.HandleFunc("/", handler)

        http.ListenAndServe(":80", nil)
}

Reference:

https://github.com/pquerna/otp

Send email with multiple attachment files

Send email with multiple attachment files

# yum install mutt

# echo "email body" | mutt -s "email subject" test@example.com -a file1.tar.gz -a file2.tar.gz

Friday, April 22, 2016

two factor authentication (TFA) implementation

two factor authentication (TFA) implementation

A great pattern that we are seeing for implementing two-factor authentication is to use the TOTP (Time-based One-time Password Algorithm) standard for the second authentication step. What is so cool about TOTP is that it is flexible enough to allow your users to generate their authentication tokens directly on their smart phones using a TOTP app like Google Authenticator or have their tokens sent to their mobile phone via SMS.

Twillio - send SMS cell phone message, voice message and Authentication API for every application

https://www.twilio.com/

GAuthify -Google® Authenticator, SMS, Voice, and Email Two-Factor Authentication in 5 minutes.

https://www.gauthify.com/

Reference:

https://www.twilio.com/blog/2013/04/add-two-factor-authentication-to-your-website-with-google-authenticator-and-twilio-sms.html

https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm#Client_Implementations

https://blog.gopheracademy.com/advent-2013/day-21-two-factor-auth/

How to get LAN IP when there is a reverse proxy server

In case of IPv4 most client addresses are masked behind NAT, on your server side you ONLY see the globally routable address which is the router's own global address.
In case of IPv6 the local address for all intents and purposes will be the same as the global one, so you'll find that in $_SERVER['REMOTE_ADDR'].
That being said, I'd also like to caution you against using the X-Forwarded-For header for ANYTHING unless it comes from a trusted source (e.g. your own reverse proxy). The client can set this header to an arbitrary value and can cause some funny or even dangerous bugs to be triggered.
On a practical note I'd like to add that using the IP address to limit how many times one can vote is a somewhat broken practice since I rent at the moment a block of 16 IP addresses and I know people who can get their hands on a full C-sized block (255 addresses) and you'll be blocking lots of people behind provider NAT's and such. In case of IPv6 everyone will have billions of addresses anyway, so the whole concept of IP blocking will be a lot more broken.
I recommend you tie the voting to something a bit more stable like phone number or e-mail registration if possible.

Reference:

http://stackoverflow.com/questions/17299870/how-to-get-the-local-ip-of-the-client-using-php

How to set up Two Factor Authentication Google Authenticator on FreeBSD

How to set up Two Factor Authentication Google Authenticator on FreeBSD

# pkg install pam_google_authenticator-20140826_1
# pkg install libqrencode

# su - johndoe -c google-authenticator

Note: A configuration file is then written to ~/.google-authenticator

# vim /etc/pam.d/sshd

auth   required   pam_unix.so   no_warn try_first_pass
auth   required  /usr/local/lib/pam_google_authenticator.so

# vim /etc/ssh/sshd_config

Match User johndoe
    AuthenticationMethods keyboard-interactive

When login:

Authenticated with partial success.

Reference:

https://sysconfig.org.uk/two-factor-authentication-with-ssh.html

Install Redis client PHP 5.4 extension for interfacing with Redis on FreeBSD

Install Redis client PHP 5.4 extension for interfacing with Redis on FreeBSD

# pecl install redis

Reference:

https://serverpilot.io/community/articles/how-to-install-the-php-redis-extension.html

https://pecl.php.net/package/redis

Install Redis 3 and Redis PHP 7 client on CentOS 7

Install Redis 3 and Redis PHP 7 client on CentOS

Redis supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs and geospatial indexes with radius queries.

Install PHP 7 through IUS Community repositories:

http://blog.ijun.org/2016/03/install-php-7-on-centos-through-ius.html

Install Redis 3 through IUS Community repositories:

# yum install redis30u

# systemctl enable redis.service

# systemctl start redis.service

# redis-cli ping

PONG

# ss -nlp | grep redis

tcp    LISTEN     0      128    127.0.0.1:6379                  *:*                   users:(("redis-server",pid=1234,fd=4))

Install Redis PHP 7 client:

# yum install gcc gcc-c++ make

# cd /tmp

# wget https://github.com/phpredis/phpredis/archive/php7.zip -O phpredis.zip

# unzip -o /tmp/phpredis.zip && mv /tmp/phpredis-* /tmp/phpredis

# cd /tmp/phpredis && phpize && ./configure && make && make install

# echo 'extension=redis.so' >> /etc/php.d/30-redis.ini

# php -r "if (new Redis() == true){ echo \"OK \r\n\"; }"

OK

Listen to other interfaces:

# vim /etc/redis.conf

bind 192.168.0.1 127.0.0.1

Restart Redis server:

# systemctl restart redis

Check the listening interfaces:

# ss -ln | grep 6379

tcp    LISTEN     0      128    127.0.0.1:6379                  *:*
tcp    LISTEN     0      128    192.168.10.41:6379                  *:*

test.php:

<?php
$test = [
  'status' => TRUE,
  'msg' => 'ok',
];

$redis = new Redis();
$redis->connect('127.0.0.1');

$redis->set('sb_test1', json_encode($test));

$test['hmm'] = 'HMM';
$redis->set('sb_test2', json_encode($test));

$sb_test1 = $redis->get('sb_test1');
$sb_test2 = $redis->get('sb_test2');

echo $sb_test1 . PHP_EOL;
echo $sb_test2 . PHP_EOL;

$allKeys = $redis->keys('*');

print_r($allKeys);

$redis->close();
?>

Reference:

https://github.com/phpredis/phpredis

https://anton.logvinenko.name/en/blog/how-to-install-redis-and-redis-php-client.html

http://redis.io/

How to return part of a string up to specified character

How to return part of a string up to specified character

package main

import (
        "fmt"
        "strings"
)

func main() {
        str := "192.168.10.5:33069"
        fmt.Printf("%s", str[0:strings.Index(str, ":")])
}

How to split a string and assign it to variables in golang?

How to split a string and assign it to variables in golang?

Solution 1:

    s := strings.Split("127.0.0.1:5432", ":")
    ip, port := s[0], s[1]
    fmt.Println(ip, port)

Solution 2:

    host, port, err := net.SplitHostPort("127.0.0.1:5432")
    fmt.Println(host, port, err)

Solution 3:

Since go is flexible an you can create your own python style split

package main

import (
    "fmt"
    "strings"
    "errors"
)

type PyString string

func main() {
    var py PyString
    py = "127.0.0.1:5432"
    ip, port , err := py.Split(":")       // Python Style
    fmt.Println(ip, port, err)
}

func (py PyString) Split(str string) ( string, string , error ) {
    s := strings.Split(string(py), str)
    if len(s) < 2 {
        return "" , "", errors.New("Minimum match not found")
    }
    return s[0] , s[1] , nil
}

Reference:

http://stackoverflow.com/questions/16551354/how-to-split-a-string-and-assign-it-to-variables-in-golang

Setting display Chinese language in Raspberry PI

Setting display Chinese language in Raspberry PI

# apt-get update

# apt-get install ttf-wqy-microhei

# dpkg-reconfigure locales

zhCN.UTF-8

zh_CN.UTF-8

Thursday, April 21, 2016

get client information IP address

  log.Printf("HERE: %s", r.Host)
  log.Printf("HERE: %s", r.RemoteAddr)
  log.Printf("HERE: %s", r.Header.Get("X-Forwarded-For"))

To reconnect the WebSocket when it's disconnected

To reconnect the WebSocket when it's disconnected

        <script type="text/javascript">
            (function() {
                var data = document.getElementById("fileData");

                //var conn = new WebSocket("ws://{{.Host}}/ws?lastMod={{.LastMod}}");
                var conn = new ReconnectingWebSocket("wss://{{.Host}}/ws?lastMod={{.LastMod}}");

                conn.onclose = function(evt) {
                    console.log('Connection closed');
                }

                conn.onmessage = function(evt) {
                    console.log('file updated');

                    var obj = JSON.parse(evt.data);
                    console.log(obj);

                    data.textContent = obj.msg;
                }
            })();
        </script>

Reference:

https://github.com/joewalnes/reconnecting-websocket

https://github.com/gorilla/websocket/blob/master/examples/filewatch/main.go

http://blog.johnryding.com/post/78544969349/how-to-reconnect-web-sockets-in-a-realtime-web-app

http://stackoverflow.com/questions/3780511/reconnection-of-client-when-server-reboots-in-websocket

Install Go golang on Raspbian Raspberry PI

Install Go golang on Raspbian Raspberry PI

Solution 1:

# apt-get install golang

Solution 2 (install newer version):

# wget https://storage.googleapis.com/golang/go1.6.1.linux-armv6l.tar.gz

# tar -C /usr/local -xzf go1.6.1.linux-armv6l.tar.gz

# vim ~/.profile

export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/work

Reference:

https://golang.org/dl/

https://golang.org/doc/install

Monday, April 18, 2016

convert avi video to HTML5 video tag supported format

convert avi video to HTML5 video tag supported format

.h264 is just a raw H.264 bytestream. That's just video content, which can be played back by sophisticated players, but usually you want to put everything into a container format, such as MPEG-4 Part 14 ("MP4").

So, run:

# ffmpeg -i test_video.avi -c:v libx264 -pix_fmt yuv420p test_video.mp4

For HTML5 progressive download you may want to move the moov atom of the MP4 container to the beginning of the file, which allows instant playback:

# ffmpeg -i test_video.avi -c:v libx264 -pix_fmt yuv420p -movflags faststart test_video.mp4

You may be interested in: What is a Codec (e.g. DivX?), and how does it differ from a File Format (e.g. MPG)?

<html>
<body>
<h1>Test Video</h1>
<video width="300" height="300" autoplay loop>
  <source src="test_video.mp4" type="video/mp4">
</video>
</body>
</html>


Reference:

http://superuser.com/questions/750811/convert-avi-into-h-264-that-works-inside-an-html5-video-tag

Friday, April 15, 2016

Install Chromium (Open Source Google Chrome) on Raspberry PI

Install Chromium (Open Source Google Chrome) on Raspberry PI

# wget http://ports.ubuntu.com/pool/universe/c/chromium-browser/chromium-browser-l10n_48.0.2564.82-0ubuntu0.15.04.1.1193_all.deb

# wget http://ports.ubuntu.com/pool/universe/c/chromium-browser/chromium-browser_48.0.2564.82-0ubuntu0.15.04.1.1193_armhf.deb

# wget http://ports.ubuntu.com/pool/universe/c/chromium-browser/chromium-codecs-ffmpeg-extra_48.0.2564.82-0ubuntu0.15.04.1.1193_armhf.deb

# dpkg -i chromium-codecs-ffmpeg-extra_48.0.2564.82-0ubuntu0.15.04.1.1193_armhf.deb

# dpkg -i chromium-browser-l10n_48.0.2564.82-0ubuntu0.15.04.1.1193_all.deb chromium-browser_48.0.2564.82-0ubuntu0.15.04.1.1193_armhf.deb

Reference:

http://www.techrepublic.com/article/five-tips-for-getting-the-most-out-of-a-raspberry-pi-3-as-a-pc/

Disable Screen Saver In Raspbian Raspberry PI

Disable Screen Saver In Raspbian Raspberry PI

Solution 1:

# vi ~/.config/lxsession/LXDE-pi/autostart

@xset s noblank
@xset s off
@xset -dpms

Note: /etc/xdg/lxsession/LXDE/autostart
Note: /etc/xdg/lxsession/LXDE-pi/autostart

Restart the computer:

# sync; reboot

Check the setting:

# xset q

Keyboard Control:
  auto repeat:  on    key click percent:  0    LED mask:  00000000
  XKB indicators:
    00: Caps Lock:   off    01: Num Lock:    off    02: Scroll Lock: off
    03: Compose:     off    04: Kana:        off    05: Sleep:       off
    06: Suspend:     off    07: Mute:        off    08: Misc:        off
    09: Mail:        off    10: Charging:    off    11: Shift Lock:  off
    12: Group 2:     off    13: Mouse Keys:  off
  auto repeat delay:  500    repeat rate:  33
  auto repeating keys:  00ffffffdffffbbf
                        fadfffefffedffff
                        9fffffffffffffff
                        fff7ffffffffffff
  bell percent:  50    bell pitch:  400    bell duration:  100
Pointer Control:
  acceleration:  20/10    threshold:  10
Screen Saver:
  prefer blanking:  no    allow exposures:  yes
  timeout:  0    cycle:  600
Colors:
  default colormap:  0x20    BlackPixel:  0x0    WhitePixel:  0xffff
Font Path:
  /usr/share/fonts/X11/100dpi/:unscaled,/usr/share/fonts/X11/Type1,/usr/share/fonts/X11/100dpi,built-ins
DPMS (Energy Star):
  Standby: 600    Suspend: 600    Off: 600
  DPMS is Disabled

Solution 2:

# cp /etc/lightdm/lightdm.conf /etc/lightdm/lightdm.conf_ORIG
# vi /etc/lightdm/lightdm.conf

[SeatDefaults]
xserver-command=X -s 0 -dpms

Reference:

https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=43932
https://www.raspberrypi.org/forums/viewtopic.php?f=91&t=136925
https://www.raspberrypi.org/forums/viewtopic.php?f=108&t=133519

Monday, April 11, 2016

How to see the command attached to a bash alias?

How to see the command attached to a bash alias?

Ctrl + Alt + E as I learned from this answer. It "expands" the currently typed command line, meaning it performs alias expansion (among other things).

# ls (Ctrl + Alt + E)

ls --color=auto

Or

# type ls

ls is aliased to `ls --color=auto'

Or

# alias ls

alias ls='ls --color=auto'

Or

# alias

alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias g='grep -riI --exclude="*\.svn*"'
alias gc='git commit -a -m "up"'
alias gg='grep -rI --exclude="*\.svn*"'
alias gp='git push'
alias gpp='git pull'
alias grep='grep --color=auto'
alias gs='git status'
alias h='history'
alias l.='ls -d .* --color=auto'
alias ll='ls -la'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'

Reference:

http://askubuntu.com/questions/102093/how-to-see-the-command-attached-to-a-bash-alias

http://stackoverflow.com/questions/211378/hidden-features-of-bash/1416125#1416125

milestone 里程碑 - an action or event marking a significant change or stage in development.

milestone 里程碑 - an action or event marking a significant change or stage in development.

cornerstone 基石 - a stone that forms the base of a corner of a building, joining two walls.

cornerstone 基石 - a stone that forms the base of a corner of a building, joining two walls.

Error: Fatal error: Can't use function return value in write context

Error: Fatal error: Can't use function return value in write context

if (empty(trim($name))) {
  continue;
}

Instead, use:

if (empty($name) || trim($name) == FALSE) {
  continue;
}

Note: Prior to PHP 5.5, empty() only supports variables; anything else will result in a parse error.

Thursday, April 7, 2016

Use SSH tunnel as a secure forward proxy

A forward proxy proxies in behalf of clients. The following setup will allow you to sit in office and SSH connect to a remote server. The remote server will act as the proxy server to forward all your requests to the destination server.

Use SSH tunnel as a secure forward proxy:

# ssh -D 8080 USER@HOST_NAME.COM

or

# ssh -N -f -D 8080 USER@HOST_NAME.COM

You will need to configure a SOCKS proxy server in Google Chrome by adding these two flags when launch Chrome:

--proxy-server="socks5://127.0.0.1:8080"
--host-resolver-rules="MAP * 0.0.0.0 , EXCLUDE 127.0.0.1"

To set up the forward proxy through Putty:

Open Putty > Connection > SSH > Tunnels:

Source Port: 8080

Destination: (leave blank)

Check "Dynamic"

Click on "Add" button

Reference:

http://www.cyberciti.biz/faq/set-up-ssh-tunneling-on-a-linux-unix-bsd-server-to-bypass-nat/

下班後,堅持自學有多難?

這一年,我無意中知道 MOOC 網,才發現原來網上還有那麼多來自世界各地各所名校的免費課程。相見恨晚,大概就是我當時的心情吧。
我今年通過 MOOC 等平台完成的課程,那些中途放棄或延遲的課程不在列:
效率類:
・Learning How to Learn: Powerful mental tools to help you master tough subjects
・Get Organized: How to be a Together Teacher
歷史類:
・中國古代歷史與人物--秦始皇
・The Modern World, Part One: Global History from 1760 to 1910
・A Brief History of Humankind
寫作類:
・Crafting an Effective Writer: Tools of the Trade (Fundamental English Writing)
・English Composition I
哲學相關:
・哈佛大學公開課:幸福課(積極心理學)
・哈佛大學公開課:公正-該如何做是好?

自學的初衷

每個人學習的理由各不相同,我當初的想法很簡單:

1.為了提升英語水平

因為工作需要,我一直都斷斷續續學英語。大學剛畢業的時候,英語渣到看《紐約時報》就像看天書一樣,直到現在我都記得那種文盲般的挫敗感。中間省略一段渣渣的苦逼困鬥過程。直到後來系統地學習了口譯、筆譯的網路課程,再加上大量的實戰,終於我的英語水平得到了質的提升。用武俠術語來說,就是有一種打通了任督二脈的感覺。
notes
▲當時學口譯、筆譯時的部分講義筆記
打通任督二脈並不等於武功化境,恰恰相反,它只是練習高級功夫的基礎。說得通俗點兒,剛剛入門而已。但有了這個基礎,就像有了一點點內功一樣,再學各種招式就會容易很多。
那個時候,我決定學點其他功夫。而 MOOC 就好像是一個江湖,這裡有很多個門派,每一個課程就是一本武功秘籍,每一本秘籍都能極大地增強功力於是,從《Learning How to Learn》開始,由此我開啓了刷課練功的徵程。(PS:因為要順帶刷英語,所以我選的課程大部分都是英文授課。)
分享一點經驗
  • 如果真想提高英語,就別看字幕;
  • 如果實在聽不懂,要看也只看英文字幕;
  • 如果連看英文字幕都很困難,那這門課對你太難了,建議換一門難度低一點的。
比如,詞彙量低於三五千的朋友,學習《English Composition I 》這門課可能會感覺吃力,學習《Crafting an Effective Writer: Tools of the Trade (Fundamental English Writing)》這門課的話則會相對難度適中一些。

2.為瞭解決一些很實際的困惑

1)明明生活順遂,為什麼不開心?

我曾經有一段時間特別焦慮,我不明白明明自己工作穩定、生活順遂,為什麼還是不開心?
我問過周圍的朋友,但是並沒有得到滿意的答案。於是我決定自己為自己診治,這也是我學習積極心理學的原因。
後來證明,我當初的這個決定是正確的,學完這個課後有種豁然開朗的感覺。

2)為什麼執行能力差?喜歡拖延?

這是我在 MOOC 上學習的第一個課程,這個課程對我來說最大的收穫就是從心理學和神經科學的角度瞭解了拖延症的成因,以及有哪些有效的方法對抗拖延症。

3)如何有效的利用時間?提高效率?

磨刀不誤砍柴工,學習一些基本的時間管理方法,對於提高學習效率和工作效率都非常有幫助。《Get Organized: How to be a Together Teacher》這個課程非常簡短,裡面介紹的一些實用表格我也一直在使用,並不斷改進摸索出適合自己的格式。

學什麼?

之前有朋友問我,你最近都在網上學些什麼,我說歷史和哲學。她很意外地說了句,你怎麼不學些有用的,我還以為你學的是跟工作相關的東西呢。
朋友的話讓我深思,在她看來學心理學和歷史是沒用的,因為這些課程對升職加薪沒什麼用,花那麼多時間,還不如學門職業技能實在。關於這點,我是這麼認為的:

1.有用VS沒用

有很多人問我,平時用英語的機會很少,學英語有沒有用?
其實,在回答這個問題之前,你應該問問自己為什麼要學英語?我覺得這個動機很重要,否則,你根本學不下去。
講一個發生在我身邊的故事。我認識一位前輩,大學學的是科技英語,畢業後分配到政府工作。他是他們單位唯一一個堅持每天學習外語的人,周圍人都笑他,有現成翻譯不用,為啥自己學。直到工作十幾年後,中國使館招募駐外大使,他從上千人中脫穎而出,人生軌跡也由此改變。
所以,有沒有用,取決於你自己怎麼看,取決於你為什麼要學。有句廣告詞說得很好,「每個人都是一座山。世上最難攀越的山,其實是自己。往上走,即便一小步,也有新高度。」只要學習,總會有收穫。很多時候,學習就是個不斷累積的過程,不僅是學英語,學其他東西也是一樣的。

2.需要VS喜歡

我們學習大部分是出於兩個目的:需要,或者喜歡。二者在效果上,很難衡量,但在持久性上,卻有顯著不同
出於實際需要學習,大多是為瞭解決一些眼下的問題,比如為了應付考試、為了答疑解惑、為了考證加薪等。但是,這樣的學習很難有長效性,就像吃藥一樣,當病好了,你就不再想吃藥。就算接下來的補藥對身體有好處,但這種需求已經不那麼迫切了。舉個例子,高考結束後,還有多少人主動學過曾經不感興趣的科目?
為了愛好學習則沒有那麼強的功利性,就像追求自己喜歡的人,未必是一定要得到那個人,也不是為了經濟上的企圖,就是喜歡,就是想努力接近她,不計成本,經年累月,樂此不疲。
因此,在我看來,與其問「我應該學什麼?」,不如問「我喜歡學什麼?」、「我想學什麼?」。

3.不知道自己喜歡什麼,怎麼辦?

不知道自己喜歡什麼的時候,只有一個方法——嘗試。這和超市的試吃是一個道理,先嘗嘗,口味對了,那就對了,口味不對,換下一個。
就我自己的經驗來說,一開始也沒有很強的方向性。只是先學一些覺得很實用,很感興趣的課程,再慢慢發現原來學習 A 還要知道 B,學習 B 還需要瞭解 C,為了更好的學習 A,其實我應該先學好 B 和 C......就這樣一步一步找到自己的方向,知道要學什麼,按什麼順序學。
所以,不要想太多,先學了再說。基本上你能堅持學下去的,就是你比較感興趣的。

怎麼學?

1.招式還是內功?

我覺得學習就跟練功一樣,分兩種:一種是練內功,一種是練招式。
招式,淺顯易學,上手快,見效快;內功,則聚沙成塔,曠日持久,進展緩慢。武功要想登峰造極則必須內外兼修,內功要深厚,招式要精通。但是,天下武功種類眾多,包羅萬象,一個人窮極一生,真正能精通的也不過幾種,所以有些功夫學些招式就可以,有些功夫則需要苦練內功。
具體來說,以我自己為例,像時間管理、《學習困難科目的實用思考方法》這種實用類的課程,我把它歸為招式類,掌握一些基本的原理和實用技巧即可,不用深入。而歷史類、哲學類的課程我比較感興趣,則投入的時間更多,學習得也相對深一些,這些屬於內功。

2.學習方法

每個人都有自己的一套學習方法。同樣一個課程,不同的人學習,收穫也會不一樣。我自己的方法就是,學完一個課程要問自己四個問
  • What——這個課講了什麼?
  • How——這個課是如何講的?
  • Critique——你同意課程中的觀點嗎?
  • Reflection——對你的生活有什麼用?
回答這些問題會助於將知識系統化,將各個知識塊有條理的組織起來。前兩個問題屬於比較淺顯的層次,認真聽課基本上都能回答上來。而人與人之間水平的差別往往表現在後兩個問題,它直接反映了你思考的層次,我認為,學課程和閱讀的方法是相通的。

怎麼堅持?

自學是件苦逼的事情,它要佔用你原本可以用來娛樂的時間。
為什麼別人看電視的時候,你要聽課?
為什麼別人刷網頁的時候,你要讀書?
為什麼別人逛淘寶的時候,你要寫筆記?
為什麼別人玩遊戲的時候,你要查資料?
為什麼一個人願意在下班後每天花上幾個小時的時間坐在書桌前學習
所以自學最難的地方既不是學什麼,也不是怎麼學,而是怎麼堅持,因為你要不斷地和自己的惰性做鬥爭,和各種各樣的誘惑做鬥爭,和輕鬆舒服的安逸做鬥爭。這麼難,怎麼辦?
方法只有一個——養成習慣。
是的,習慣。
學生時代,我一直很喜歡寫作。工作後,這個愛好就擱置了。直到從今年開始,我又重新開始學習寫作課程、加入寫作訓練營、堅持在公眾號分享自己的讀書筆記、學習體會,並一直堅持到現在。究其原因還是因為從小就有寫日記的習慣,學生時代大大小小的日記本有幾十本,儘管工作後這個習慣就慢慢放棄了,但是一種熱愛一旦養成習慣,就容易順著這種慣性堅持下去。很多人很難做到每天學習,卻可以很輕鬆地堅持每天刷牙。為什麼?
這就是習慣的力量。
畢業很多年後,我才終於明白為什麼當年那些學霸可以天天學習而我卻做不到的原因了,因為學習對人家來說就跟刷牙一樣自然啊!(一個老學渣多麼痛的領悟~)

為什麼一定要自學?

這一點放在最後講,是因為我認為自學最大的意義就是——給未來的自己投資。如果說有什麼投資是只賺不賠的話,那麼自學無疑就是這樣的投資。而且,這項投資越早越好,它會為你帶來這樣一些收益:

1.解鎖加速學習的技能

自學,未必能夠給你帶來好的工作、好的運氣、好的報酬。但是,學習到一定程度,你會解鎖一個關鍵技能——融會貫通,之後你的學習速度就會呈幾何增長。
think
根據 Barbara Oakley 教授的觀點,我們的知識是壓縮成「塊」(chunk)的,每一個塊都有一個固定的神經迴路,某個塊用得越頻繁,那一部分的神經迴路就會越牢固,就像小路越走越平坦一樣。而我們學習新知識時,神經會自發地選擇那些平坦的路徑,也就是會與已有的知識塊進行連接,這個過程被稱為「融會貫通」或者「觸類旁通」(transfer)。所以,當你的知識塊越來越多,體積越來越大之後,後面學習新的東西就會越來越容易。有人說,學習到一定程度就會產生加速度,原理就是在這裡。
更重要的是,原有的知識塊很容易產生意想不到的連接,帶來驚喜。每個人都看到蘋果落地,卻只有牛頓發現了地心引力。這種「頓悟」,其實就是融會貫通的意外成果。

2.獲得階梯式的成長

想起網路上的一個笑話:一個人跑去問老闆「我都有十年工作經驗了,為什麼您還不給我漲薪水呢?」,老闆回答說 「你是有十年工作經驗呢,還是把一年工作經驗用了十年呢?」。
這個故事我深有體會,工作的前幾年往往是一個人成長最快的時期,當你一切都能得心應手、熟悉應對時,很容易進入一種平和的溫水煮青蛙的時期。
當一個人的學習曲線開始趨平的時候,要特別小心,這其實是一個危險的信號。誰都喜歡安逸,可是安逸的後果就是一年的工作經驗一不小心就用了十年。想要避免這種情況,唯一的辦法就是持續不斷的學習。

3.遇見更優秀的自己

知乎有一個問答給我留下了深刻的印象
題主問:
你在知乎上究竟學會了什麼?
其中最高票的回答是這樣的:
小時候不努力學習,長大了就只能給別人點讚。
一句話不知戳中了多少人的心坎。不多解釋,你們懂的。
總結:
這一年對我來說,是收穫頗豐的一年。我覺得最大的收穫其實不是知識本身,而是學習習慣的養成,學習方法的改進,以及學習認識的提高。寫到這裡,突然覺得好心塞,為什麼我20歲的時候,不懂得這些呢?
或許,人的一生總是要走些彎路的吧。這種時候,大概也只能大丈夫了。即使現在有些晚了,我還是想說:趁著牙好胃口好,趕緊學習吧!
sfh

【視野,決定你的腳步!】你這麼努力,怎麼都沒人看見?其實 你需要做的是

職場上,人人都很忙,人人都想提升自己。
但是,為什麼有些人提升快,有些人提升慢?

有些人可能平時和你一樣在玩,
但是他們卻進步很明顯。

而你已經很努力在拼搏,

但是自我提升卻一直緩慢不前…


這到底是為什麼?...

這可能不是和付出的程度有關,

而跟你的視野有關。


那些有視野的人廣闊的人一般有這些特徵:
1、對事情有預見性
2、情緒穩定能夠控制自己
3、內心有邏輯,判斷有依據
4、訊息快速篩選和過濾的技能

你可能嘗試過很多方法達到他們這樣的境界,

是不是都失敗了?

· 每週工作超過 60 小時,投入的工作
· 每月讀書 4 本
· 不花太多時間在消耗時間的網站
· 除非緊急事務,不使用手機
· 每天午休,晚上 11 點前睡覺
· 每週都做計劃和總結
· 堅持一週 3 次以上運動

上述習慣 可以讓你變更好、更健康,
卻無法讓你成長更快...

那你應該再做些什麼呢?

你需要做的是....

1. 獨立思考

讓自己變得有思想
如果你沒有思想,看過那麼多的書也只是看過而已。
如果你沒有思想,學了那麼多東西也只是儲存而已。
只有你培養了自己的獨立思考能力,
才能給你帶來更大視野,
有了更大的視野
才會有更多的知識、見解、技能、經驗…

2. 挑戰恐懼

主動去做你一直排斥、厭惡之事,
這樣你的人生就會更不同。

舉個例子:
如果你很討厭和頑皮的小朋友相處,
那你就勇敢的嘗試和他們相處一下午;
與你討厭的小朋友至少進行一次心平靜氣的深入交流,
然後你會發現很多你完全意想不到的事情,
再與小朋友的家長交流,
這一個下午比你看書一定收獲的多很多。

你們肯定曾有類似的經歷和感受,對吧?



3. 保持開放

任何內心狹隘之人,是永遠不可能視野開闊,
夜郎自大就形容這樣的人。

所以放下自己那些固有偏見吧,
如果你被你自己圍住,不管你走到哪裡,
見到多大的天地,都只是你內心那些小小天地。

4. 了解江湖

如果你只懂得知識,對這個花花世界都不了解,
縱然你行走萬里,視野也依然狹窄。

如果你能理解江湖,縱然你視野的廣度有所不及,
但是你能達到的深度是別人難以企及的,
你所看到的風景也是別人無法看到的。
所以說,了解江湖是多麼重要的一件事情。

5. 借鑒智慧

製作一份你所感興趣的主題問卷,
發放給你身邊的人(人數越多越好),
問卷內容中設計一些開放問題。

你放心,只要有人認真回答,
你一定能夠收到意想不到的答案,
而且這些答案一定能夠讓你對這件事情
有了重新的認識,視野瞬間被打開有沒有?

這個世界有...

50%的事情不在於你知,而在於你覺。

人生如此,人力資源也如此!


Reference:

http://www.cmoney.tw/notes/note-detail.aspx?nid=52837

Monday, April 4, 2016

Fix the SPF neutral message when you send email through Amazon's EC2 instance

If you got the following SPF neutral message when you send email through Amazon's EC2 instance:

Received-SPF: neutral (google.com: 51.11.171.201 is neither permitted nor denied by best guess record for domain of root@ip-171-31-11-41.us-west-2.compute.internal) client-ip=51.11.171.201;

Solution:

Set host name:

# hostnamectl set-hostname host1.example.com

# hostnamectl status

# cat /etc/hostname

Append the following string at the bottom of the file to ensure that the hostname is preserved between restarts/reboots:

# vim /etc/cloud/cloud.cfg

preserve_hostname: true

More info: https://aws.amazon.com/premiumsupport/knowledge-center/linux-static-hostname-rhel7-centos7/

Contact Amazon to add a reverse DNS record:

You can contact Amazon to request to remove email sending limitations:

https://aws.amazon.com/forms/ec2-email-limit-rdns-request

Input your Elastic IP Address 1: 51.11.171.201

Input Reverse DNS Record for EIP 1: host1.example.com (this is the Amazon EC2 instance you will be using to send out emails).

Create a sample script:

# vim test_send.php

<?php
$from    = 'info@example.com';
$to      = 'someone@abc.com';

$subject = 'test subject 6';
$message = 'test subject 6';

$headers = 'From: ' . $from . "\r\n" .
    'Reply-To: ' . $from . "\r\n" .
    'X-Mailer: PHP/' . phpversion();

mail($to, $subject, $message, $headers);
?>

Show email original:

Received-SPF: pass (google.com: best guess record for domain of user1@host1.example.com designates 51.11.171.201 as permitted sender) client-ip=51.11.171.201;

Reference:

http://aws.amazon.com/ec2/faqs/#Are_there_any_limitations_in_sending_email_from_EC2_instances
https://aws.amazon.com/premiumsupport/knowledge-center/route-53-reverse-dns/

Sunday, April 3, 2016

main(): listen tcp :443: bind: permission denied

main(): listen tcp :443: bind: permission denied

To bind to ports below 1024 you need to run the program as root. There are some workarounds:

http://stackoverflow.com/questions/413807/is-there-a-way-for-non-root-processes-to-bind-to-privileged-ports-1024-on-l/414258#414258

Simple golang HTTPS web server example

Simple golang HTTPS web server example

Generate a Private Key and a CSR:

Use this method if you want to use HTTPS (HTTP over TLS) to secure your Apache HTTP or Nginx web server, and you want to use a Certificate Authority (CA) to issue the SSL certificate. The CSR that is generated can be sent to a CA to request the issuance of a CA-signed SSL certificate. If your CA supports SHA-2, add the -sha256 option to sign the CSR with SHA-2.

# openssl req -newkey rsa:2048 -nodes -subj "/C=US/ST=New York/L=Brooklyn/O=Example Brooklyn Company/CN=examplebrooklyn.com" -keyout mydomain.key -out mydomain.csr

Note: The -newkey rsa:2048 option specifies that the key should be 2048-bit, generated using the RSA algorithm.
Note: The -nodes option specifies that the private key should not be encrypted with a pass phrase.
Note: The -new option, which is not included here but implied, indicates that a CSR is being generated.

Generate a Self-Signed Certificate:

Use this method if you want to use HTTPS (HTTP over TLS) to secure your Apache HTTP or Nginx web server, and you do not require that your certificate is signed by a CA.

This command creates a 2048-bit private key (domain.key) and a self-signed certificate (domain.crt) from scratch:

# openssl req -newkey rsa:2048 -nodes -subj "/C=US/ST=New York/L=Brooklyn/O=Example Brooklyn Company/CN=examplebrooklyn.com" -keyout mydomain.key -x509 -days 365 -out mydomain.crt

Version 1 - srv.go:

package main

import (
    "io"
    "net/http"
    "log"
)

func HelloServer(w http.ResponseWriter, req *http.Request) {
    io.WriteString(w, "hello, world!\n")
}

func main() {
    http.HandleFunc("/hello", HelloServer)
    err := http.ListenAndServeTLS(":443", "mydomain.crt", "mydomain.key", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

Run the code:

# go run srv.go

Then, go to:

https://192.168.5.1/hello

Use curl command to access:

# curl -k https://192.168.5.1/hello

hello, world!

Forces curl to use SSL version 3 when negotiating with a remote SSL server:

# curl -k -3 https://192.168.5.1/hello

Here, we try to access port 443 by HTTP protocol:

# curl -k http://192.168.5.1:443/hello | od -A n -t x1

15 03 01 00 02 02 0a

the above sequence of bytes 15 03 01 00 02 02 0a which is rendered/handled by the browser.

Which, according to https://code.google.com/p/go/issues/detail?id=2253, is TLS for "I didn't understand what you said."

Version 2:

package main

import (
    "fmt"
    "net/http"
)

const (
    PORT       = ":8443"
    PRIV_KEY   = "./mydomain.key"
    CERT_KEY = "./mydomain.crt"
)

func rootHander(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Nobody should read this.")
}

func main() {
    http.HandleFunc("/hello", rootHander)
    err := http.ListenAndServeTLS(PORT, CERT_KEY, PRIV_KEY, nil)
    if err != nil {
        fmt.Printf("main(): %s\n", err)
    }
}

Then, go to:

https://192.168.5.1/hello

Reference:

https://gist.github.com/denji/12b3a568f092ab951456

http://stackoverflow.com/questions/23494082/golang-listenandservetls-returns-data-when-not-using-https-in-the-browser

https://github.com/nareix/tls-example

https://en.wikipedia.org/wiki/X.509

https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs

Generate the private key and certificate

Generate the private key and certificate

Run gen.go:

# go run gen.go

package main

import (
 "crypto/x509"
 "crypto/x509/pkix"
 "crypto/rsa"
 "crypto/rand"
 "math/big"
 "io/ioutil"
 "log"
 "time"
)

func main() {
 ca := &x509.Certificate{
  SerialNumber: big.NewInt(1653),
  Subject: pkix.Name{
   Country: []string{"China"},
   Organization: []string{"Yjwt"},
   OrganizationalUnit: []string{"YjwtU"},
  },
  NotBefore: time.Now(),
  NotAfter: time.Now().AddDate(10,0,0),
  SubjectKeyId: []byte{1,2,3,4,5},
  BasicConstraintsValid: true,
  IsCA: true,
  ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
  KeyUsage: x509.KeyUsageDigitalSignature|x509.KeyUsageCertSign,
 }

 priv, _ := rsa.GenerateKey(rand.Reader, 1024)
 pub := &priv.PublicKey
 ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv)
 if err != nil {
  log.Println("create ca failed", err)
  return
 }
 ca_f := "ca.pem"
 log.Println("write to", ca_f)
 ioutil.WriteFile(ca_f, ca_b, 0777)

 priv_f := "ca.key"
 priv_b := x509.MarshalPKCS1PrivateKey(priv)
 log.Println("write to", priv_f)
 ioutil.WriteFile(priv_f, priv_b, 0777)

 cert2 := &x509.Certificate{
  SerialNumber: big.NewInt(1658),
  Subject: pkix.Name{
   Country: []string{"China"},
   Organization: []string{"Fuck"},
   OrganizationalUnit: []string{"FuckU"},
  },
  NotBefore: time.Now(),
  NotAfter: time.Now().AddDate(10,0,0),
  SubjectKeyId: []byte{1,2,3,4,6},
  ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
  KeyUsage: x509.KeyUsageDigitalSignature|x509.KeyUsageCertSign,
 }
 priv2, _ := rsa.GenerateKey(rand.Reader, 1024)
 pub2 := &priv2.PublicKey
 cert2_b, err2 := x509.CreateCertificate(rand.Reader, cert2, ca, pub2, priv)
 if err2 != nil {
  log.Println("create cert2 failed", err2)
  return
 }

 cert2_f := "cert2.pem"
 log.Println("write to", cert2_f)
 ioutil.WriteFile(cert2_f, cert2_b, 0777)

 priv2_f := "cert2.key"
 priv2_b := x509.MarshalPKCS1PrivateKey(priv2)
 log.Println("write to", priv2_f)
 ioutil.WriteFile(priv2_f, priv2_b, 0777)

 ca_c, _ := x509.ParseCertificate(ca_b)
 cert2_c, _ := x509.ParseCertificate(cert2_b)

 err3 := cert2_c.CheckSignatureFrom(ca_c)
 log.Println("check signature", err3 == nil)
}

server.go:

package main

import (
 "crypto/rand"
 "crypto/tls"
 "log"
 "net"
 "crypto/x509"
 "io/ioutil"
)

func main() {

 ca_b, _ := ioutil.ReadFile("ca.pem")
 ca, _ := x509.ParseCertificate(ca_b)
 priv_b, _ := ioutil.ReadFile("ca.key")
 priv, _ := x509.ParsePKCS1PrivateKey(priv_b)

 pool := x509.NewCertPool()
 pool.AddCert(ca)

 cert := tls.Certificate{
  Certificate: [][]byte{ ca_b },
  PrivateKey: priv,
 }

 config := tls.Config{
  ClientAuth: tls.RequireAndVerifyClientCert,
  Certificates: []tls.Certificate{cert},
  ClientCAs: pool,
 }
 config.Rand = rand.Reader
 service := "0.0.0.0:443"
 listener, err := tls.Listen("tcp", service, &config)
 if err != nil {
  log.Fatalf("server: listen: %s", err)
 }
 log.Print("server: listening")

 for {
  conn, err := listener.Accept()
  if err != nil {
   log.Printf("server: accept: %s", err)
   break
  }
  defer conn.Close()
  log.Printf("server: accepted from %s", conn.RemoteAddr())
  go handleClient(conn)
 }
}

func handleClient(conn net.Conn) {
 defer conn.Close()
 buf := make([]byte, 512)
 for {
  log.Print("server: conn: waiting")
  n, err := conn.Read(buf)
  if err != nil {
   if err != nil {
    log.Printf("server: conn: read: %s", err)
   }
   break
  }

  tlscon, ok := conn.(*tls.Conn)
  if ok {
   state := tlscon.ConnectionState()
   sub := state.PeerCertificates[0].Subject
   log.Println(sub)
  }

  log.Printf("server: conn: echo %q\n", string(buf[:n]))
  n, err = conn.Write(buf[:n])

  n, err = conn.Write(buf[:n])
  log.Printf("server: conn: wrote %d bytes", n)

  if err != nil {
   log.Printf("server: write: %s", err)
   break
  }
 }
 log.Println("server: conn: closed")
}

client.go:

package main

import (
 "crypto/tls"
 "crypto/x509"
 "fmt"
 "io"
 "io/ioutil"
 "log"
)

func main() {
 cert2_b, _ := ioutil.ReadFile("cert2.pem")
 priv2_b, _ := ioutil.ReadFile("cert2.key")
 priv2, _ := x509.ParsePKCS1PrivateKey(priv2_b)

 cert := tls.Certificate{
  Certificate: [][]byte{ cert2_b },
  PrivateKey: priv2,
 }

 config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
 conn, err := tls.Dial("tcp", "127.0.0.1:443", &config)
 if err != nil {
  log.Fatalf("client: dial: %s", err)
 }
 defer conn.Close()
 log.Println("client: connected to: ", conn.RemoteAddr())

 state := conn.ConnectionState()
 for _, v := range state.PeerCertificates {
  fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
  fmt.Println(v.Subject)
 }
 log.Println("client: handshake: ", state.HandshakeComplete)
 log.Println("client: mutual: ", state.NegotiatedProtocolIsMutual)

 message := "Hello\n"
 n, err := io.WriteString(conn, message)
 if err != nil {
  log.Fatalf("client: write: %s", err)
 }
 log.Printf("client: wrote %q (%d bytes)", message, n)

 reply := make([]byte, 256)
 n, err = conn.Read(reply)
 log.Printf("client: read %q (%d bytes)", string(reply[:n]), n)
 log.Print("client: exiting")
}

Reference:

https://github.com/nareix/tls-example/blob/master/gen.go

Saturday, April 2, 2016

Go quick start

# wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz

# tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz

# vim ~/.bash_profile

export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/work

# source ~/.bash_profile

# go version

go version go1.6 linux/amd64

# mkdir ~/work/src/github.com/username/hello

# vim test.go

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}

# go run test.go

hello, world

Reference:

https://golang.org/doc/install