понедельник, 26 декабря 2016 г.

Хэш-функция ГОСТ 34.11-2012 на Python 2.7

Понадобилось тут, сам не знаю зачем... Так как я пару лет назад разбирался с этим вопросом, то решил не мудрить и почти дословно переписал.

Файл stribog.py:
#!/usr/bin/python
# -"- coding: UTF-8 -"-

class entry:
    """Вычисление хэш-функции по алгоритму ГОСТ 34.11-2012"""



    def __init__(self):
        pass



    def XOR (self, A, B):
        return [a^b for a, b in zip(A, B)]



    def plus (self, A, B):

        result = [0]*64

        i, remain = 63, 0
        while(i >= 0):
            s = A[i] + B[i] + remain
            remain = (s >> 8) if s >= 256 else 0
            result[i] = s & 0xFF
            i -= 1

        return result



    def P (self, A):

        tau = ( \
            0,  8, 16, 24, 32, 40, 48, 56, \
            1,  9, 17, 25, 33, 41, 49, 57, \
            2, 10, 18, 26, 34, 42, 50, 58, \
            3, 11, 19, 27, 35, 43, 51, 59, \
            4, 12, 20, 28, 36, 44, 52, 60, \
            5, 13, 21, 29, 37, 45, 53, 61, \
            6, 14, 22, 30, 38, 46, 54, 62, \
            7, 15, 23, 31, 39, 47, 55, 63)

        result = [0]*64
        for i in xrange(0, 64):
            result[i] = A[tau[i]]

        return result



    def S (self, A):
        Pi = ( \
            252, 238, 221,  17, 207, 110,  49,  22, 251, 196, 250, \
            218,  35, 197,   4,  77, 233, 119, 240, 219, 147,  46, \
            153, 186,  23,  54, 241, 187,  20, 205,  95, 193, 249, \
             24, 101,  90, 226,  92, 239,  33, 129,  28,  60,  66, \
            139,   1, 142,  79,   5, 132,   2, 174, 227, 106, 143, \
            160,   6,  11, 237, 152, 127, 212, 211,  31, 235,  52, \
             44,  81, 234, 200,  72, 171, 242,  42, 104, 162, 253, \
             58, 206, 204, 181, 112,  14,  86,   8,  12, 118,  18, \
            191, 114,  19,  71, 156, 183,  93, 135,  21, 161, 150, \
             41,  16, 123, 154, 199, 243, 145, 120, 111, 157, 158, \
            178, 177,  50, 117,  25,  61, 255,  53, 138, 126, 109, \
             84, 198, 128, 195, 189,  13,  87, 223, 245,  36, 169, \
             62, 168,  67, 201, 215, 121, 214, 246, 124,  34, 185, \
              3, 224,  15, 236, 222, 122, 148, 176, 188, 220, 232, \
             40,  80,  78,  51,  10,  74, 167, 151,  96, 115,  30, \
              0,  98,  68,  26, 184,  56, 130, 100, 159,  38,  65, \
            173,  69,  70, 146,  39,  94,  85,  47, 140, 163, 165, \
            125, 105, 213, 149,  59,   7,  88, 179,  64, 134, 172, \
             29, 247,  48,  55, 107, 228, 136, 217, 231, 137, 225, \
             27, 131,  73,  76,  63, 248, 254, 141,  83, 170, 144, \
            202, 216, 133,  97,  32, 113, 103, 164,  45,  43,   9, \
             91, 203, 155,  37, 208, 190, 229, 108,  82,  89, 166, \
            116, 210, 230, 244, 180, 192, 209, 102, 175, 194,  57, \
            75,  99, 182)

        result = [0]*64
        for i in xrange(0, 64):
            result[i] = Pi[A[i]]

        return result



    def L (self, A):

        l = ( \
            0x8e, 0x20, 0xfa, 0xa7, 0x2b, 0xa0, 0xb4, 0x70, \
            0x47, 0x10, 0x7d, 0xdd, 0x9b, 0x50, 0x5a, 0x38, \
            0xad, 0x08, 0xb0, 0xe0, 0xc3, 0x28, 0x2d, 0x1c, \
            0xd8, 0x04, 0x58, 0x70, 0xef, 0x14, 0x98, 0x0e, \
            0x6c, 0x02, 0x2c, 0x38, 0xf9, 0x0a, 0x4c, 0x07, \
            0x36, 0x01, 0x16, 0x1c, 0xf2, 0x05, 0x26, 0x8d, \
            0x1b, 0x8e, 0x0b, 0x0e, 0x79, 0x8c, 0x13, 0xc8, \
            0x83, 0x47, 0x8b, 0x07, 0xb2, 0x46, 0x87, 0x64, \
             \
            0xa0, 0x11, 0xd3, 0x80, 0x81, 0x8e, 0x8f, 0x40, \
            0x50, 0x86, 0xe7, 0x40, 0xce, 0x47, 0xc9, 0x20, \
            0x28, 0x43, 0xfd, 0x20, 0x67, 0xad, 0xea, 0x10, \
            0x14, 0xaf, 0xf0, 0x10, 0xbd, 0xd8, 0x75, 0x08, \
            0x0a, 0xd9, 0x78, 0x08, 0xd0, 0x6c, 0xb4, 0x04, \
            0x05, 0xe2, 0x3c, 0x04, 0x68, 0x36, 0x5a, 0x02, \
            0x8c, 0x71, 0x1e, 0x02, 0x34, 0x1b, 0x2d, 0x01, \
            0x46, 0xb6, 0x0f, 0x01, 0x1a, 0x83, 0x98, 0x8e, \
             \
            0x90, 0xda, 0xb5, 0x2a, 0x38, 0x7a, 0xe7, 0x6f, \
            0x48, 0x6d, 0xd4, 0x15, 0x1c, 0x3d, 0xfd, 0xb9, \
            0x24, 0xb8, 0x6a, 0x84, 0x0e, 0x90, 0xf0, 0xd2, \
            0x12, 0x5c, 0x35, 0x42, 0x07, 0x48, 0x78, 0x69, \
            0x09, 0x2e, 0x94, 0x21, 0x8d, 0x24, 0x3c, 0xba, \
            0x8a, 0x17, 0x4a, 0x9e, 0xc8, 0x12, 0x1e, 0x5d, \
            0x45, 0x85, 0x25, 0x4f, 0x64, 0x09, 0x0f, 0xa0, \
            0xac, 0xcc, 0x9c, 0xa9, 0x32, 0x8a, 0x89, 0x50, \
             \
            0x9d, 0x4d, 0xf0, 0x5d, 0x5f, 0x66, 0x14, 0x51, \
            0xc0, 0xa8, 0x78, 0xa0, 0xa1, 0x33, 0x0a, 0xa6, \
            0x60, 0x54, 0x3c, 0x50, 0xde, 0x97, 0x05, 0x53, \
            0x30, 0x2a, 0x1e, 0x28, 0x6f, 0xc5, 0x8c, 0xa7, \
            0x18, 0x15, 0x0f, 0x14, 0xb9, 0xec, 0x46, 0xdd, \
            0x0c, 0x84, 0x89, 0x0a, 0xd2, 0x76, 0x23, 0xe0, \
            0x06, 0x42, 0xca, 0x05, 0x69, 0x3b, 0x9f, 0x70, \
            0x03, 0x21, 0x65, 0x8c, 0xba, 0x93, 0xc1, 0x38, \
             \
            0x86, 0x27, 0x5d, 0xf0, 0x9c, 0xe8, 0xaa, 0xa8, \
            0x43, 0x9d, 0xa0, 0x78, 0x4e, 0x74, 0x55, 0x54, \
            0xaf, 0xc0, 0x50, 0x3c, 0x27, 0x3a, 0xa4, 0x2a, \
            0xd9, 0x60, 0x28, 0x1e, 0x9d, 0x1d, 0x52, 0x15, \
            0xe2, 0x30, 0x14, 0x0f, 0xc0, 0x80, 0x29, 0x84, \
            0x71, 0x18, 0x0a, 0x89, 0x60, 0x40, 0x9a, 0x42, \
            0xb6, 0x0c, 0x05, 0xca, 0x30, 0x20, 0x4d, 0x21, \
            0x5b, 0x06, 0x8c, 0x65, 0x18, 0x10, 0xa8, 0x9e, \
             \
            0x45, 0x6c, 0x34, 0x88, 0x7a, 0x38, 0x05, 0xb9, \
            0xac, 0x36, 0x1a, 0x44, 0x3d, 0x1c, 0x8c, 0xd2, \
            0x56, 0x1b, 0x0d, 0x22, 0x90, 0x0e, 0x46, 0x69, \
            0x2b, 0x83, 0x88, 0x11, 0x48, 0x07, 0x23, 0xba, \
            0x9b, 0xcf, 0x44, 0x86, 0x24, 0x8d, 0x9f, 0x5d, \
            0xc3, 0xe9, 0x22, 0x43, 0x12, 0xc8, 0xc1, 0xa0, \
            0xef, 0xfa, 0x11, 0xaf, 0x09, 0x64, 0xee, 0x50, \
            0xf9, 0x7d, 0x86, 0xd9, 0x8a, 0x32, 0x77, 0x28, \
             \
            0xe4, 0xfa, 0x20, 0x54, 0xa8, 0x0b, 0x32, 0x9c, \
            0x72, 0x7d, 0x10, 0x2a, 0x54, 0x8b, 0x19, 0x4e, \
            0x39, 0xb0, 0x08, 0x15, 0x2a, 0xcb, 0x82, 0x27, \
            0x92, 0x58, 0x04, 0x84, 0x15, 0xeb, 0x41, 0x9d, \
            0x49, 0x2c, 0x02, 0x42, 0x84, 0xfb, 0xae, 0xc0, \
            0xaa, 0x16, 0x01, 0x21, 0x42, 0xf3, 0x57, 0x60, \
            0x55, 0x0b, 0x8e, 0x9e, 0x21, 0xf7, 0xa5, 0x30, \
            0xa4, 0x8b, 0x47, 0x4f, 0x9e, 0xf5, 0xdc, 0x18, \
             \
            0x70, 0xa6, 0xa5, 0x6e, 0x24, 0x40, 0x59, 0x8e, \
            0x38, 0x53, 0xdc, 0x37, 0x12, 0x20, 0xa2, 0x47, \
            0x1c, 0xa7, 0x6e, 0x95, 0x09, 0x10, 0x51, 0xad, \
            0x0e, 0xdd, 0x37, 0xc4, 0x8a, 0x08, 0xa6, 0xd8, \
            0x07, 0xe0, 0x95, 0x62, 0x45, 0x04, 0x53, 0x6c, \
            0x8d, 0x70, 0xc4, 0x31, 0xac, 0x02, 0xa7, 0x36, \
            0xc8, 0x38, 0x62, 0x96, 0x56, 0x01, 0xdd, 0x1b, \
            0x64, 0x1c, 0x31, 0x4b, 0x2b, 0x8e, 0xe0, 0x83)

        result = [0]*64

        i = 7
        while(i >= 0):
            n = 0
            while(n <= 7):
                p = 63
                j = 7
                while(j >= 0):
                    k = 0
                    while(k <= 7):
                        if ((A[i*8+j]>>k) & 1):
                            result[i*8+n] ^= l[p*8+n]
                        p-=1
                        k+=1
                    j-=1
                n+=1
            i-=1

        return result



    def X (self, K, A):
        return self.XOR(K, A)



    def LPS (self, A):
        return self.L(self.P(self.S(A)))



    def E (self, K, M):

        C = ( \
            (0xb1, 0x08, 0x5b, 0xda, 0x1e, 0xca, 0xda, 0xe9, 0xeb, 0xcb, 0x2f, 0x81, 0xc0, 0x65, 0x7c, 0x1f, \
            0x2f, 0x6a, 0x76, 0x43, 0x2e, 0x45, 0xd0, 0x16, 0x71, 0x4e, 0xb8, 0x8d, 0x75, 0x85, 0xc4, 0xfc, \
            0x4b, 0x7c, 0xe0, 0x91, 0x92, 0x67, 0x69, 0x01, 0xa2, 0x42, 0x2a, 0x08, 0xa4, 0x60, 0xd3, 0x15, \
            0x05, 0x76, 0x74, 0x36, 0xcc, 0x74, 0x4d, 0x23, 0xdd, 0x80, 0x65, 0x59, 0xf2, 0xa6, 0x45, 0x07), \
            \
            (0x6f, 0xa3, 0xb5, 0x8a, 0xa9, 0x9d, 0x2f, 0x1a, 0x4f, 0xe3, 0x9d, 0x46, 0x0f, 0x70, 0xb5, 0xd7, \
            0xf3, 0xfe, 0xea, 0x72, 0x0a, 0x23, 0x2b, 0x98, 0x61, 0xd5, 0x5e, 0x0f, 0x16, 0xb5, 0x01, 0x31, \
            0x9a, 0xb5, 0x17, 0x6b, 0x12, 0xd6, 0x99, 0x58, 0x5c, 0xb5, 0x61, 0xc2, 0xdb, 0x0a, 0xa7, 0xca, \
            0x55, 0xdd, 0xa2, 0x1b, 0xd7, 0xcb, 0xcd, 0x56, 0xe6, 0x79, 0x04, 0x70, 0x21, 0xb1, 0x9b, 0xb7), \
            \
            (0xf5, 0x74, 0xdc, 0xac, 0x2b, 0xce, 0x2f, 0xc7, 0x0a, 0x39, 0xfc, 0x28, 0x6a, 0x3d, 0x84, 0x35, \
            0x06, 0xf1, 0x5e, 0x5f, 0x52, 0x9c, 0x1f, 0x8b, 0xf2, 0xea, 0x75, 0x14, 0xb1, 0x29, 0x7b, 0x7b, \
            0xd3, 0xe2, 0x0f, 0xe4, 0x90, 0x35, 0x9e, 0xb1, 0xc1, 0xc9, 0x3a, 0x37, 0x60, 0x62, 0xdb, 0x09, \
            0xc2, 0xb6, 0xf4, 0x43, 0x86, 0x7a, 0xdb, 0x31, 0x99, 0x1e, 0x96, 0xf5, 0x0a, 0xba, 0x0a, 0xb2), \
            \
            (0xef, 0x1f, 0xdf, 0xb3, 0xe8, 0x15, 0x66, 0xd2, 0xf9, 0x48, 0xe1, 0xa0, 0x5d, 0x71, 0xe4, 0xdd, \
            0x48, 0x8e, 0x85, 0x7e, 0x33, 0x5c, 0x3c, 0x7d, 0x9d, 0x72, 0x1c, 0xad, 0x68, 0x5e, 0x35, 0x3f, \
            0xa9, 0xd7, 0x2c, 0x82, 0xed, 0x03, 0xd6, 0x75, 0xd8, 0xb7, 0x13, 0x33, 0x93, 0x52, 0x03, 0xbe, \
            0x34, 0x53, 0xea, 0xa1, 0x93, 0xe8, 0x37, 0xf1, 0x22, 0x0c, 0xbe, 0xbc, 0x84, 0xe3, 0xd1, 0x2e), \
            \
            (0x4b, 0xea, 0x6b, 0xac, 0xad, 0x47, 0x47, 0x99, 0x9a, 0x3f, 0x41, 0x0c, 0x6c, 0xa9, 0x23, 0x63, \
            0x7f, 0x15, 0x1c, 0x1f, 0x16, 0x86, 0x10, 0x4a, 0x35, 0x9e, 0x35, 0xd7, 0x80, 0x0f, 0xff, 0xbd, \
            0xbf, 0xcd, 0x17, 0x47, 0x25, 0x3a, 0xf5, 0xa3, 0xdf, 0xff, 0x00, 0xb7, 0x23, 0x27, 0x1a, 0x16, \
            0x7a, 0x56, 0xa2, 0x7e, 0xa9, 0xea, 0x63, 0xf5, 0x60, 0x17, 0x58, 0xfd, 0x7c, 0x6c, 0xfe, 0x57), \
            \
            (0xae, 0x4f, 0xae, 0xae, 0x1d, 0x3a, 0xd3, 0xd9, 0x6f, 0xa4, 0xc3, 0x3b, 0x7a, 0x30, 0x39, 0xc0, \
            0x2d, 0x66, 0xc4, 0xf9, 0x51, 0x42, 0xa4, 0x6c, 0x18, 0x7f, 0x9a, 0xb4, 0x9a, 0xf0, 0x8e, 0xc6, \
            0xcf, 0xfa, 0xa6, 0xb7, 0x1c, 0x9a, 0xb7, 0xb4, 0x0a, 0xf2, 0x1f, 0x66, 0xc2, 0xbe, 0xc6, 0xb6, \
            0xbf, 0x71, 0xc5, 0x72, 0x36, 0x90, 0x4f, 0x35, 0xfa, 0x68, 0x40, 0x7a, 0x46, 0x64, 0x7d, 0x6e), \
            \
            (0xf4, 0xc7, 0x0e, 0x16, 0xee, 0xaa, 0xc5, 0xec, 0x51, 0xac, 0x86, 0xfe, 0xbf, 0x24, 0x09, 0x54, \
            0x39, 0x9e, 0xc6, 0xc7, 0xe6, 0xbf, 0x87, 0xc9, 0xd3, 0x47, 0x3e, 0x33, 0x19, 0x7a, 0x93, 0xc9, \
            0x09, 0x92, 0xab, 0xc5, 0x2d, 0x82, 0x2c, 0x37, 0x06, 0x47, 0x69, 0x83, 0x28, 0x4a, 0x05, 0x04, \
            0x35, 0x17, 0x45, 0x4c, 0xa2, 0x3c, 0x4a, 0xf3, 0x88, 0x86, 0x56, 0x4d, 0x3a, 0x14, 0xd4, 0x93), \
            \
            (0x9b, 0x1f, 0x5b, 0x42, 0x4d, 0x93, 0xc9, 0xa7, 0x03, 0xe7, 0xaa, 0x02, 0x0c, 0x6e, 0x41, 0x41, \
            0x4e, 0xb7, 0xf8, 0x71, 0x9c, 0x36, 0xde, 0x1e, 0x89, 0xb4, 0x44, 0x3b, 0x4d, 0xdb, 0xc4, 0x9a, \
            0xf4, 0x89, 0x2b, 0xcb, 0x92, 0x9b, 0x06, 0x90, 0x69, 0xd1, 0x8d, 0x2b, 0xd1, 0xa5, 0xc4, 0x2f, \
            0x36, 0xac, 0xc2, 0x35, 0x59, 0x51, 0xa8, 0xd9, 0xa4, 0x7f, 0x0d, 0xd4, 0xbf, 0x02, 0xe7, 0x1e), \
            \
            (0x37, 0x8f, 0x5a, 0x54, 0x16, 0x31, 0x22, 0x9b, 0x94, 0x4c, 0x9a, 0xd8, 0xec, 0x16, 0x5f, 0xde, \
            0x3a, 0x7d, 0x3a, 0x1b, 0x25, 0x89, 0x42, 0x24, 0x3c, 0xd9, 0x55, 0xb7, 0xe0, 0x0d, 0x09, 0x84, \
            0x80, 0x0a, 0x44, 0x0b, 0xdb, 0xb2, 0xce, 0xb1, 0x7b, 0x2b, 0x8a, 0x9a, 0xa6, 0x07, 0x9c, 0x54, \
            0x0e, 0x38, 0xdc, 0x92, 0xcb, 0x1f, 0x2a, 0x60, 0x72, 0x61, 0x44, 0x51, 0x83, 0x23, 0x5a, 0xdb), \
            \
            (0xab, 0xbe, 0xde, 0xa6, 0x80, 0x05, 0x6f, 0x52, 0x38, 0x2a, 0xe5, 0x48, 0xb2, 0xe4, 0xf3, 0xf3, \
            0x89, 0x41, 0xe7, 0x1c, 0xff, 0x8a, 0x78, 0xdb, 0x1f, 0xff, 0xe1, 0x8a, 0x1b, 0x33, 0x61, 0x03, \
            0x9f, 0xe7, 0x67, 0x02, 0xaf, 0x69, 0x33, 0x4b, 0x7a, 0x1e, 0x6c, 0x30, 0x3b, 0x76, 0x52, 0xf4, \
            0x36, 0x98, 0xfa, 0xd1, 0x15, 0x3b, 0xb6, 0xc3, 0x74, 0xb4, 0xc7, 0xfb, 0x98, 0x45, 0x9c, 0xed), \
            \
            (0x7b, 0xcd, 0x9e, 0xd0, 0xef, 0xc8, 0x89, 0xfb, 0x30, 0x02, 0xc6, 0xcd, 0x63, 0x5a, 0xfe, 0x94, \
            0xd8, 0xfa, 0x6b, 0xbb, 0xeb, 0xab, 0x07, 0x61, 0x20, 0x01, 0x80, 0x21, 0x14, 0x84, 0x66, 0x79, \
            0x8a, 0x1d, 0x71, 0xef, 0xea, 0x48, 0xb9, 0xca, 0xef, 0xba, 0xcd, 0x1d, 0x7d, 0x47, 0x6e, 0x98, \
            0xde, 0xa2, 0x59, 0x4a, 0xc0, 0x6f, 0xd8, 0x5d, 0x6b, 0xca, 0xa4, 0xcd, 0x81, 0xf3, 0x2d, 0x1b), \
            \
            (0x37, 0x8e, 0xe7, 0x67, 0xf1, 0x16, 0x31, 0xba, 0xd2, 0x13, 0x80, 0xb0, 0x04, 0x49, 0xb1, 0x7a, \
            0xcd, 0xa4, 0x3c, 0x32, 0xbc, 0xdf, 0x1d, 0x77, 0xf8, 0x20, 0x12, 0xd4, 0x30, 0x21, 0x9f, 0x9b, \
            0x5d, 0x80, 0xef, 0x9d, 0x18, 0x91, 0xcc, 0x86, 0xe7, 0x1d, 0xa4, 0xaa, 0x88, 0xe1, 0x28, 0x52, \
            0xfa, 0xf4, 0x17, 0xd5, 0xd9, 0xb2, 0x1b, 0x99, 0x48, 0xbc, 0x92, 0x4a, 0xf1, 0x1b, 0xd7, 0x20))

        result = self.X(K, M)

        i = 0
        while(i < 12):
            result = self.LPS(result)
            K = self.XOR(K, C[i])
            K = self.LPS(K)
            result = self.X(K, result)
            i+=1

        return result



    def g (self, N, h, m):

        result = self.XOR(h, N)
        result = self.LPS(result)

        result = self.E(result, m)
        result = self.XOR(result, h)
        result = self.XOR(result, m)

        return result



    def FromString (self, string, hashtype):

        h = [1 if hashtype == 256 else 0]*64

        e = [0]*64
        N = [0]*64
        Z = [0]*64
        m = [0]*64

        start = 0
        while start + 64 <= len(string):
            for i in xrange(0, 64):
                m[63-i] = ord(string[start+i])

            h = self.g(N, h, m)

            e[62] = (512 >> 8);
            e[63] = (512 & 0xFF);
            N = self.plus(N, e);

            Z = self.plus(Z, m);

            start += 64

        sz = len(string)-start
        m = [0]*64
        for i in xrange(0, sz):
            m[63-i] = ord(string[start+i])
        m[64-sz-1] = 1

        h = self.g(N, h, m)
        e[62] = (sz*8)>>8
        e[63] = (sz*8)&0xFF
        N = self.plus(N, e)
        Z = self.plus(Z, m)

        e[62] = 0
        e[63] = 0
        h = self.g(e, h, N)

        h = self.g(e, h, Z)

        return ''.join(reversed([('%0.2X' % a) for a in h][:(32 if hashtype == 256 else 64)]))

Пользоваться этим просто:
#!/usr/bin/python
# -"- coding: UTF-8 -"-

import stribog

print "test 1"
print stribog.entry().FromString('012345678901234567890123456789012345678901234567890123456789012', 512)
print stribog.entry().FromString('012345678901234567890123456789012345678901234567890123456789012', 256)

print "test 2"
print stribog.entry().FromString(open('stribog_test2.txt').read(), 512)
print stribog.entry().FromString(open('stribog_test2.txt').read(), 256)

четверг, 22 декабря 2016 г.

Linux: Продолжаю осваивать git

Сегодня после долгого перерыва опять подступился к git.
После прошлых экспериментов остался веб-доступ к репозиторию. Попытался сделать папочку для нового проекта, затем склонировал проект на локальную машину, там чего-то добавил/поменял, сделал коммит, затем попытался выполнить команду git push origin master и... получил ошибку:
error: unpack failed: unpack-objects abnormal exit
error: RPC failed; result=18, HTTP code = 200
fatal: The remote end hung up unexpectedly
error: error in sideband demultiplexer
Оказалось, забыл на сервере раздать права на папку с проектом для учетки, под которой работает веб-сервер.

Попутно выяснилось, что если веб-сервер использует самоподписанный кривой сертификат, то git при общении с ним обламывается с ошибкой: fatal: unable to access 'https://user@server/git/myProject/': SSL certificate problem: self signed certificate. С этим можно побороться так: стащить с сервера файл *.crt и использовать его сначала в команде git clone:
git -c http.sslCAInfo=myCertificate.crt clone https://user@server/git/myProject/
А затем вообще сконфигурировав локальную копию проекта на использование этого сертификата командой:
git config --local --add http.sslCAInfo myCertificate.crt

Также попутно пришлось вспомнить, что прокси можно прописать глобально командой:
git config --global http.proxy http://proxy:port
или индивидуально для проекта, выполнив в папке с его локальной копией команду:
git config --local --add http.proxy http://myproxy:8888

вторник, 20 декабря 2016 г.

Windows 7: чудеса с DNS

Неожиданно как-то странно себя повела рабочая станция под управлением Windows 7 Professional.
Ни на один сайт не заходит, при попытке в командной строке сделать nslookup google.com пишет *** UnKnown не удается найти google.com: No response from server, при этом окружающие хосты пингуются нормально, и на саму эту рабочую станцию попасть, скажем, через RDP не составляет труда. В то же время, установленный на машине сервер RAdmin не запускается.

Помогло такое:
netsh winsock reset catalog
netsh int ipv4 reset reset.log
netsh int ipv6 reset reset.log

четверг, 1 декабря 2016 г.

Шаблоны в веб-разработке

Предположим, мы делаем первые шаги в сайтостроении. Мы знаем HTML в каком-то объеме, достаточном для того, чтобы создать несколько HTML-документов на интересующую нас тематику, и на первый взгляд этого должно хватить, чтобы получился симпатичненький статический веб-сайт.

Но, как и всё вокруг, наш веб-сайт имеет тенденцию разрастаться и видоизменяться. Из одной веб-страницы мы делаем другую копированием соответствующего HTML-файла и изменением его содержимого, и этих файлов становится всё больше и больше. Рано или поздно мы заметим тревожные симптомы:
  1. различий внутри двух близких по времени файлов наберётся дай бог процентов на 10;
  2. HTML-код на сайте от страницы к странице становится всё более и более разнородным, потому что мы узнаём новые приёмы, а старый код исправлять лень или некогда;
  3. всё труднее поддерживать единообразное оформление, например, чтобы изменить шапку страниц или название одного пункта навигации, приходится править кучу HTML-файлов, валяющихся на сайте с начала времён.
И постепенно становится ясно: пришла пора задуматься о том, как упорядочить этот нарастающий хаос.

К сожалению, это прозрение приходит с осознанием того, что придётся переделать пару дюжин уже существующих и пусть не вписывающихся в ваше новое видение, но хорошо работающих веб-страниц. Что ж, на это надо потратить время - оно того стоит.

Будем использовать модельный пример. Пусть у нас есть сайт, стоящий из трёх веб-страниц, соответствующие HTML-файлы которых выглядят так:

Файл index.html
<html>
<title>Главная страница</title>
<body>
<div>Шапка сайта</div>
<div><a href="first.html">На первую страницу</a> | <a href="second.html">На вторую страницу</a><div>
</body>
</html>

Файл first.html
<html>
<title>Первая страница</title>
<body>
<div>Шапка сайта</div>
<div><a href="/">На главную</a> | <a href="second.html">На вторую страницу</a></div>
<div>Содержимое первой страницы</div>
</body>
</html>

Файл second.html
<html>
<title>Вторая страница</title>
<body>
<div>Шапка сайта</div>
<div><a href="/">На главную</a> | <a href="first.html">На первую страницу</a></div>
<div>Содержимое второй страницы</div>
</body>
</html>

Итак, приступим.

Первым делом надо произвести аудит наших наработок и выделить те части, которые должны остаться неизменными от страницы к странице. Это наверняка будет шапка сайта, меню, оформление и т.п. Дальше нужно будет выбрать путь из множества вариантов, доступных (или потенциально доступных) веб-разработчику.

Например, можно творчески использовать тег <iframe>, выделив шапку сайта и меню в отдельный фрейм. Наш модельный сайт в этом случае может выглядеть так:

Файл header.html
<html>
<body>
<div>Шапка сайта</div>
<div>
<a target="_top" href="/">На главную</a> |
<a target="_top" href="first.html">На первую страницу</a> |
<a target="_top" href="second.html">На вторую страницу</a>
</div>
</body>
</html>

Файл index.html
<html>
<title>Главная страница</title>
<body>
<div><iframe src="header.html"></div>
</body>
</html>

Файл first.html
<html>
<title>Первая страница</title>
<body>
<div><iframe src="header.html"></div>
<div>Содержимое первой страницы</div>
</body>
</html>

Файл second.html
<html>
<title>Вторая страница</title>
<body>
<div><iframe src="header.html"></div>
<div>Содержимое второй страницы</div>
</body>
</html>

Теперь, если мы захотим вместо "Шапка сайта" написать "Кепка сайта", нам не придётся править все три HTML-файла, достаточно поменять лишь страницу header.html. Но на самом деле это - не очень хороший подход. Так не удастся избавиться от большей части однообразного содержимого HTML-файлов: заголовков, разметки и т.п. Поэтому более разумным вариантом представляется использование шаблонов.

Что такое шаблон в веб-разработке? Ближайшим его аналогом в материальном мире является бланк. Да-да, та самая отпечатанная типографским способом разлинованная бумажка, вписав в которую свои ФИО и другие реквизиты получаешь заявление на загранпаспорт, квитанцию о переводе денежных средств, товарную накладную и т.п. Так что при этом подходе нам понадобится разработать некую заготовку, в которую останется лишь вписать в нужный момент нужные данные. Но кто будет эти данные вписывать?

Тут опять-таки возможны два варианта:
  1. наполнять шаблон данными на сервере и отдавать клиенту готовый документ;
  2. передать шаблон и данные клиенту, и пусть его браузер сам их компонует как хочет.

Первый путь предполагает наличие на стороне сервера какого-то механизма для обработки клиентских запросов. Стоит отметить, что в наше цивилизованное время сами веб-серверы научились в какой-то мере производить необходимые манипуляции со страницами перед отправкой их клиенту. Мы имеем в виду технологию Server Side Includes (SSI).

В частности, наш модельный пример с использованием SSI принимает вид:

Файл header.shtml
<div>Шапка сайта</div>
<div>
<a href="/">На главную</a> |
<a href="first.shtml">На первую страницу</a> |
<a href="second.shtml">На вторую страницу</a>
</div>

Файл index.shtml
<html>
<title>Главная страница</title>
<body>
<!--#include file="header.shtml"-->
</body>
</html>

Файл first.shtml
<html>
<title>Первая страница</title>
<body>
<!--#include file="header.shtml"-->
<div>Содержимое первой страницы</div>
</body>
</html>

Файл second.shtml
<html>
<title>Вторая страница</title>
<body>
<!--#include file="header.shtml"-->
<div>Содержимое второй страницы</div>
</body>
</html>

Как видим, с помощью этого механизма можно собирать веб-страницы из разрозненных частей, избегая таким образом избыточного их дублирования. А с учетом наличия средств, обеспечивающих ветвление, SSI оказывается безусловно интересным вариантом в некоторых случаях.

Более тонкой настройке поддаются программы или скрипты для обработки шаблонов, написанные на каком-нибудь подходящем языке программирования. Это может быть PHP, Python, ASP, node.js, черт, дьявол, да всё что угодно вплоть до bash-скриптов. У каждого из этих языков есть свои достаточно мощные инструменты и модули для работы с шаблонами. Мы не будем углубляться в эти дебри, а покажем на нашем модельном примере сам принцип обработки шаблонов с помощью серверных приложений.

Пусть у нас на сайте установлен Python. Перепишем наш пример следующим образом:

Файл template.html
<html>
<title>%(title)s</title>
<body>
<div>Шапка сайта</div>
<div>
<a href="/">На главную</a> |
<a href="first.py">На первую страницу</a> |
<a href="second.py">На вторую страницу</a>
</div>
<div>%(content)s</div>
</body>
</html>

Файл index.py
from mod_python import Cookie, apache

def index(req):

  return open('template.html').read() % \
    {
    'title' : 'Главная страница',
    'content' : ''
    }

Файл first.py
from mod_python import Cookie, apache

def index(req):

  return open('template.html').read() % \
    {
    'title' : 'Первая страница',
    'content' : 'Содержимое первой страницы'
    }

Файл second.py
from mod_python import Cookie, apache

def index(req):

  return open('template.html').read() % \
    {
    'title' : 'Вторая страница',
    'content' : 'Содержимое второй страницы'
    }

Как видим, в этом примере данные удалось полностью отвязать от их отображения, что является несомненным плюсом. Правда, при этом увеличилась нагрузка на сервер и пришлось изучить Python, но оно того стоит. Весь HTML код оказался в файле шаблона, и если мы, скажем, захотим сверстать наш сайт как-то по-другому, нам не придётся переписывать кучу файлов HTML. Конечно, большое множество изменений, связанных с оформлением, можно сделать с помощью стилевых файлов. Но, скажем, подключить какой-нибудь javascript или установить тег META с помощью CSS, насколько я в курсе, не получится.

Второй путь - предоставить браузеру на стороне клиента строить из шаблона и данных отображаемую у клиента страницу - проще всего реализуется, как мне кажется, с помощью технологии по имени XSLT. В этом случае мы передаем клиенту данные и рецепт, по которому ему предстоит из данных собрать итоговую страницу. Проиллюстрируем этот подход на нашем многострадальном модельном примере:

Файл template.xsl
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/root">

    <html>
    <title><xsl:value-of select="title"/></title>
    <body>
    <div>Шапка сайта</div>
    <div>
    <a href="/">На главную</a> |
    <a href="first.xml">На первую страницу</a> |
    <a href="second.xml">На вторую страницу</a>
    </div>
    <div><xsl:value-of select="content"/></div>
    </body>
    </html>

  </xsl:template>

<xsl:stylesheet>

Файл index.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="template.xsl"?>
<root>
<title>Главная страница</title>
<content/>
</root>

Файл first.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="template.xsl"?>
<root>
<title>Первая страница</title>
<content>Содержимое первой страницы</content>
</root>

Файл second.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="template.xsl"?>
<root>
<title>Вторая страница</title>
<content>Содержимое второй страницы</content>
</root>

Этот подход лично мне импонирует по многим причинам. Во-первых, с подобного сайта легко можно брать и обрабатывать данные машинным способом. Во-вторых, сервер не нагружается задачей превращения шаблона в итоговую страницу. В-третьих, данные опять-таки полностью отделены от оформления. Но тут есть пара подводных камней, один большой и один поменьше.

Как оказалось, не все браузеры поддерживают эту технологию. Например, браузер, встроенный в Android, до 4-й версии XML+XSLT не понимал. И, самое главное: сайты, сверстанные с использованием XML+XSLT, крайне неохотно индексируются поисковиками. То есть, боты преобразованием XSLT не заморачиваются. Можно слегка поправить ситуацию, если привести XML-файлы с данными к виду "почти HTML", но это - явный костыль.

Резюмирую. Задача уменьшения количества повторений одного и того же HTML кода по всему сайту в принципе решаема, причем путей её решения может быть несколько. Все они, конечно, требуют дополнительных знаний, но прогрессом, как известно, движет лень, и вышеупомянутые технологии - лишнее тому доказательство.

четверг, 10 ноября 2016 г.

Python: округление

Выяснилась интересная особенность. В python 2.7 округление выполняется как учили в школе, где число x.5 округляется до (x+1):
>>> print round(0.5), round(1.5)
1.0 2.0

В python 3.3 поведение этой функции изменилось:
>>> print ( round(0.5), round(1.5) )
0 2
то есть, во-первых, по умолчанию возвращается целое число, и, главное, выполняется bankers rounding (банковское округление, в котором x.5 округляется до ближайшего четного).

Как эту печаль обойти штатными срадствами, не нашел, поэтому пока пользуюсь таким костыликом:
def round27 (number, ndigits=0):
    result = round(number, ndigits)
    if abs(result) >= abs(number): return result
    delta = abs(number) - abs(result)
    if ndigits >= 0:
        multiplier = 10.0**(ndigits+1)
        if abs(number)*multiplier-abs(result)*multiplier == 5.0:
            result = number + (delta if number >= 0 else -delta)
    else:
        multiplier = 10.0**(-ndigits-1)
        if abs(number)-abs(result) == 5.0*multiplier:
            result = number + (delta if number >= 0 else -delta)
    return result

Вообще, оказывается, есть даже стандарт IEEE 754, в котором все эти правила округления указаны. Надо глянуть, может, и ГОСТ найдётся.

UPD. Нашелся вот такой документ: СТ СЭВ 543-77 Числа. Правила записи и округления
Тут как раз округление по правилу "половины дальше от нуля".

четверг, 3 ноября 2016 г.

Linux: Как пережать PDF

Предположим, у нас есть некий pdf-документ, который надо разослать по электронной почте N адресатам. Казалось бы, не проблема: создавай N писем с вложенным документом и отправляй, благо, слепить на коленке софт под это дело - задача нехитрая.

Основная проблема - размер этого самого pdf-документа. При массовой рассылке объем переданной через почтовый сервер информации возрастёт в N раз, поэтому возникает логичное желание ужать пересылаемый документ как только можно. Сам документ получен со сканера, то есть фактически является увесистым набором изображений. С другой стороны, к счастью, тут качество не сильно важно, достаточно, чтобы текст читался, да подписи с печатями были различимы. Короче, задача сформулировалась так: получить из многостраничного сканированного документа черно-белый факс убогого качества, но читаемый, в формате pdf.

Никуда не деться, взял в качестве образца пятистраничный файл input.pdf размером 923801 байт и принялся экспериментировать.

Должен сказать, что самый правильный путь решения проблемы: воспользоваться Adobe Acrobat-ом. Эта чудесная софтина содержит команду "Optimize scanned PDF", которая при определенных настройках каким-то волшебным образом умудряется превращать исходный файл в жалкие 97416 байтов! К сожалению, в данный момент лицензионного Adobe Acrobat-а под рукой нет, поэтому приходится выкручиваться тем, что есть.

Первый вариант, который советуют знающие люди - использовать ghostscript. Соответствующая команда выглядит так:
gs \
    -sDEVICE=pdfwrite \
    -dCompatibilityLevel=1.4 \
    -dPDFSETTINGS=/screen \
    -dNOPAUSE \
    -dQUIET \
    -dBATCH \
    -sOutputFile=output.pdf 
    input.pdf
Результат получается вполне читаемым, но объем не сильно уменьшился и составил 548870 байтов.

Второй найденный на просторах инета вариант реализует идею уменьшить количество цветов до минимума:
#!/bin/sh

inFile=input.pdf
outFile=output.pdf

pdfimages "$inFile" scan1
for a in scan1*.p*m; do
   convert -depth 2 -colorspace gray $a ${a%.*}.tiff
done

tiffcp scan1*.tiff "scan1-.tiff"
tiff2pdf "scan1-.tiff" -o "$outFile" -p A4 -F -z

rm scan1*.*
Как видим, здесь используется несколько утилит: ImageMagick с его хитрой командой convert, а также LibTIFF (утилиты tiffcp и tiff2pdf). Результат: 380120 байтов. Неплохо, но далеко от рекорда.

Далее возникла мысль вообще перекодировать pdf в монохромный режим. Сделать это просто, достаточно чуть-чуть модифицировать тело цикла в предыдущем варианте:
#!/bin/sh

inFile=input.pdf
outFile=output.pdf

pdfimages "$inFile" scan1
for a in scan1*.p*m; do
    convert -white-threshold 100% -monochrome   $a   ${a%.*}.tiff
done

tiffcp scan1*.tiff "scan1-.tiff"
tiff2pdf "scan1-.tiff" -o "${outFile%.*}.${outFile##*.}" -p A4 -F -z

rm scan1*.*
Результат всё ещё читаем и имеет размер 252072 байта.

Ещё один вариант, который стоило попытаться сделать - это уменьшить в два раза размеры отсканированных изображений перед тем, как переводить их в монохром. Идея показалась здравой, но, будучи применена "в лоб", дала совершенно нечитаемый результат.

Можно попробовать ещё один подход: попытаться векторизовать содержащиеся в исходном файле изображения. Оказывается, есть специальная утилита для этого дела: Potrace. Правда, если применить её так:
#!/bin/sh

inFile=input.pdf
outFile=output.pdf

pdfimages "$inFile" scan1
for a in scan1*.p*m; do
    mkbitmap -f 2 -s 1 -x -t 0.8  $a  -o ${a%.*}-step1.${a##*.}
done

potrace -b pdf -t 5 scan1-*-step1.p*m -o "$outFile"

rm scan1*.*
то результат хоть и выглядит забавно, но оказался совсем тяжелым, 1440526 байтов.

Однако нет худа без добра: в дистрибутиве Potrace обнаружилась замечательная утилита mkbitmap, которая позволяет подправить изображение при преобразовании в монохром. Если её полезные свойства скомбинировать с предыдущими вариантами, то получается такой скрипт:
#!/bin/sh

inFile=input.pdf
outFile=output.pdf

pdfimages "$inFile" scan1
for a in scan1*.p*m; do
   mkbitmap -f 2 -s 1 -x -t 0.8          $a                -o ${a%.*}-step1.pbm
   convert -depth 2 -colorspace gray     ${a%.*}-step1.pbm    ${a%.*}-step2.tiff
   convert -resize 50%                   ${a%.*}-step2.tiff   ${a%.*}-step3.tiff
done

tiffcp scan1*-step3.tiff "scan1-.tiff"
tiff2pdf "scan1-.tiff" -o "$outFile" -p A4 -F -z

rm scan1*.*
и это оказался лучший результат: 180664 байта при всё ещё различимом тексте.

Вывод: конечно, до рекорда Adobe Acrobat-а ещё очень далеко, но, как говорится, забесплатно и уксус сладкий. Буду пока пользоваться этим.

вторник, 1 ноября 2016 г.

ECMAScript 6

Ух ты!.. Немного питона, немного си-шарпа, немного пхп...
Обзор базовых возможностей ES6

Навскидку понравились классы, области видимости переменных через let/const, строковые шаблоны, многострочные литералы, итераторы, вскрытия массивов с помощью троеточия.

Хочу попробовать!

воскресенье, 16 октября 2016 г.

C#: Двухфакторная аутентификация без смартфона

Возникла тут интересная проблема. Если вкратце, то одна из "программ-от-знакомства-с-которыми-нельзя-отказаться" после смены версии вдруг вообразила себя крутой финансовой системой и затребовала двухфакторную аутентификацию. Не знаю, чем уж там разработчики соблазнили заказчиков, но теперь после ввода логина-пароля эта чудесная софтина показывает некий QR-код и предлагает пользователю установить на смартфон Google Authenticator, которому надо скормить этот её QR-код, чтобы этот GA начал генерировать PIN-коды, которые теперь будут дополнительно требоваться при входе. Короче, если хочешь поработать, покупай смартфон.

Меня, гордого пользователя Samsung SGH-X100 с периодически отваливающимся от старости аккумулятором, такое положение дел устроить никак не могло. Поэтому пришлось познакомиться с этим Google Authenticator-ом поближе. Как я понял, механизм тут такой.

Пусть есть клиент и сервер, и этот сервер хочет предоставить клиенту дополнительную защиту от взлома, даже если у этого самого клиента уведут пароль. Сервер предоставляет клиенту при первом входе некое уникальное число X0, которое клиент запоминает у себя и бережно хранит.

Когда клиент хочет аутентифицироваться на сервере, он берёт это число X0, текущее время T и применяет к ним описанную в стандарте детерминированную функцию F(X0, T), которая на выходе выдаёт PIN-код, передаваемый серверу. Сервер, в свою очередь, проделывает у себя ту же операцию, сравнивает полученный и рассчитанный PIN-коды и, если они совпадают, позволяет клиенту работать.

Как видим, алгоритм сильно зависит от того, насколько хорошо синхронизированы часы на сервере и у клиента. Чтобы упростить задачу, в алгоритме используется не количество, например, миллисекунд, прошедших с начала эпохи UNIX, а количество 30-секундных интервалов. То есть, допускается разбег между часами клиента и сервера в пределах 30 секунд.

Уникальное же число X0 как раз и предоставляется сервером клиенту в виде картинки с QR-кодом - видимо, для пущей безопасности.

Вооружившись этими знаниями, написал простенькую программку, используя которую можно считать этот QR-код с экрана и получать PIN-коды для двухфакторной аутентификации. Ну, то есть, хрен бы я чего написал, если бы не поиск Google, подсказки http://stackoverflow.com и волшебная библиотека MessagingToolkit.QRCode.dll.

Вот тут научился работать с получением всемирного координированного времени по протоколу NTP:
http://stackoverflow.com/questions/1193955/how-to-query-an-ntp-server-using-c

Вот тут рассказали, как получить снимок части экрана:
http://stackoverflow.com/questions/5049122/capture-the-screen-shot-using-net

Вот тут научили работать с base 32:
http://stackoverflow.com/questions/641361/base32-decoding

Вот тут нашелся рабочий код для Google Authenticator:
http://stackoverflow.com/questions/6421950/is-there-a-tutorial-on-how-to-implement-google-authenticator-in-net-apps

Вот тут (кажется) скачал библиотеку MessagingToolkit.QRCode.dll:
http://osdn.net/projects/sfnet_qrreader/downloads/MessagingToolkit.QRCode.dll/

Ну и, собственно, сам результат:

Программа

Исходники

UPD 2017-01-20: Как оказалось, история не закончилась. Нашлись QR-коды, на которых MessagingToolkit сбоит. Например, если QR-код содержит картинку-логотип. Пришлось перетащить проект на другую библиотеку, ZXing.NET

Программа (ZXing)

Исходники (ZXing)

А тут - небольшая памятка, как этой библиотекой пользоваться.

четверг, 13 октября 2016 г.

Python: проблема с подключением к MSSQL при наличии триггера входа

История такова. Жил-был под линуксом скрипт, написанный когда-то на Python 2.7 и использующий pymssql, который успешно подключался к Microsoft SQL Server. В один прекрасный момент подключаться он перестал с ошибкой:
Logon failed for login 'huhmuh' due to trigger execution. DB-Lib error message 20018, severity 14:
General SQL Server error: Check messages from the SQL Server
DB-Lib error message 20002, severity 9:
Adaptive Server connection failed

Расследование выявило интересные вещи. Оказывается, pymssql использовал FreeTDS, который, собственно, и заглючил. Обмен FreeTDS можно подслушать примерно так:
import os
import pymssql

os.environ['TDSDUMP'] = 'stdout'

conn = pymssql.connect(host='mySQLserver', user='huhmuh', password='', charset='UTF-8', database='master', appname='my Application')
conn.close()
Анализ дампа выявил, что используется FreeTDS версии 0.91. Обновление до версии 1.00 не помогло.

При этом в соседнем каталоге лежит другая программа, написанная на C, которая через unixODBC получает данные с того же сервера без всяких проблем. Дальнейшие раскопки показали, что этот самый unixODBC взаимодействует с сервером через Microsoft SQL Server Native Client.

В конце концов оказалось, что на сервере разработчики добавили триггер входа. При наличии этого триггера FreeTDS не может нормально подключиться к базе данных.

Как перенацелить pymssql на использование нативного клиента, найти не удалось, поэтому пришлось перетаскивать скрипт на pyodbc. Тут тоже нашлась пара подводных камней. Самый крупный из которых - ошибка при выполнении пакета команд в одном запросе: Error: No results. Previous SQL was not a query. В этом случае виноваты, во-первых, всякие сообщения сервера "1 rows affected", которые легко подавить командой SET NOCOUNT ON в начале запроса, и, во-вторых, результаты работы команд print, причудливо раскиданных разработчиками внутри хранимых процедур, используемых в нашем пакете. Эти последние подавить нельзя, поэтому пришлось часть пакетов переписывать в виде группы отдельных запросов.

В общем, тот ещё опыт.

четверг, 8 сентября 2016 г.

Windows XP: переподключить удалённый компьютер к домену

Дано: компьютер по имени myWS под управлением WinXP, который пашет в другом городе с отключенной учёткой локального админа, но зато в домене myDOMAIN. Для удалённого доступа на этом компьютере установлен Remote Administrator с - какое счастье!- парольной авторизацией. В какой-то момент компьютер теряет связь с доменом, выдавая на всякие runas ошибку "Не удалось установить доверительные отношения между этой рабочей станцией и основным доменом" (The trust relationship between this workstation and the primary domain failed). Как-то надо привести его в чувства.

Решение такое.

1. При помощи Remote Administrator-а заходим на этот компьютер в режиме telnet.

2. Из Windows XP Support Tools закидываем на этот компьютер программу netdom.exe

3. В командной строке выполняем команды:
netdom remove myWS /Domain:myDOMAIN /UserD:myDOMAIN\Admin /PasswordD:***
netdom join myWS /Domain:myDOMAIN /UserD:myDOMAIN\Admin /PasswordD:***

3'. Вроде как есть альтернативный вариант - сбросить учётку компьютера на контроллере (на сервере в контекстном меню "Reset account") и на самом компьютере командой:
netdom reset myWS /Domain:myDOMAIN /UserO:myDOMAIN\Admin /PasswordO:***
но проверить его не удалось: всё заработало и так.
(кстати, забавно, почему там UserO, а не UserD ?)

Ну и в качестве бонуса - памятка, как посмотреть адреса контроллеров домена:
nslookup
> set type=all
> _ldap._tcp.dc._msdcs

UPD 2018-08-03: Как быть в Windows 7.

Выяснилось, что для того, чтобы в системе появился netdom.exe, надо проделать некоторую работу:

1. Установить некие Remote Server Administration Tools for Windows 7.

2. После установки включить соответствующую компоненту. Для этого идём в Панель управления -> Программы -> Программы и компоненты -> Включение или отключение компонентов Windows -> Средства удаленного админинстрирования сервера -> Средства администрирования ролей -> Средства доменных служб ActiveDirecotry и служб ActiveDirectory -> Средства доменных служб ActiveDirectory и ставим галочку напротив пункта "Оснастки и программы командной строки доменных служб ActiveDirectory". Тогда в c:\windows\system32 появляется долгожданная утилита netdom.exe.

Такое подозрение, что всё это через командную строку RAdmin-а проделать не удастся. Так что, видимо, надо заранее закидывать на борт эти самые "Средства удаленного администрирования сервера" - на всякий случай.

понедельник, 29 августа 2016 г.

Brother HL-2132: Сброс счетчика тонера

Неожиданно принтер Brother HL-2132 решил, что он тоже вертолёт, и отказался печатать. Горит зелёная лампочка напротив слова "Toner" - и всё, на задания не откликается, бумагу не подхватывает. По-хорошему, ему бы надо заменить картридж, но в это время суток его сложно достать. Решение нашлось такое:

1. Открыть крышку принтера и оставить её в таком положении до дальнейших распоряжений.
2. Выключить принтер.
3. Нажать кнопку "Go" и, удерживая её, включить принтер. Все индикаторы принтера должны светиться.
4. Отпустить кнопку "Go".
5. Нажать кнопку "Go" два раза.
6. Подождать.
7. Нажать кнопку "Go" пять* раз.
8. Индикатор тонера должен погаснуть.
9. Индикатор бумаги должен либо гореть, либо мигать.
10. Закрыть крышку принтера. Должен остаться гореть только индикатор "Ready".
11. Выключить принтер и включить его снова.

* - некоторые источники, правда, утверждают, что шесть раз, но мне помогло пять.

четверг, 11 августа 2016 г.

IE: удаление учетных данных авторизации на прокси

Пусть у нас есть Internet Explorer 8, который ходит в интернет через прокси, и этот прокси требует авторизацию для того, чтобы решить, пропускать ли через себя кого попало. И пусть мы сначала ввели какие-то учетные данные, и теперь браузер не запрашивает нас, а пытается с этими сохраненными учетными данными авторизовываться.

Чтобы его заставить забыть эти учетные данные, есть некий рецепт:

1. В командной строке выполнить команду:
control userpasswords2

2. В появившемся окне перейти во вкладку "Дополнительно", нажать кнопку "Управление паролями" и удалить сохраненный пароль для прокси-сервера.

firefox: разрешить неподписанные расширения

Оказывается, просто:

1. В адресной строке вбить about:config

2. В открывшемся списке опций найти xpinstall.signatures.required и сделать её false

пятница, 17 июня 2016 г.

MSSQL: подслушать обмен клиентского приложения с сервером

На самом деле иногда бывает сильно интересно, какие sql-запросы посылает клиентское приложение. Если дело происходит под линуксом, через unixodbc, то там легко - приписываем в odbcinst.ini (который находится где-то то ли в /etc, то ли в /usr/local/etc) пару строк:
[ODBC]
Trace=Yes
TraceFile=/home/huh-muh/sql.log
и в своем домашнем каталоге лицезреем весь обмен. Под windows, думалось, должны быть аналогичные механизмы, но всё оказалось чуть-чуть сложнее.

Во-первых, есть хорошая статья о том, как включить трассировку для клиентов SQL Servera. Вкратце:

1. Стаскиваем и разворачиваем самораспаковывающийся архив по этой ссылке.

2. В реестре находим (или создаём) ветку
HKEY_LOCAL_MACHINE\Software\Microsoft\BidInterface\Loader
в которой создаём ключ с именем ":Path" (двоеточие тут вроде как существенно) и значением:
"%SYSTEMROOT%\Microsoft.NET\Framework\v2.0.50727\ADONETDiag.dll", если хотим трассировать ADO.NET
или
"%SYSTEMROOT%\SYSTEM32\msdaDiag.dll", если хотим трассировать прочих клиентов (mdac, dao и т.п.)

3. в кучке каталогов из п.1 находим MOF_Files и в нем выполняем команду:
mofcomp all.mof (а чего мелочиться-то)
эта команда компилирует и регистрирует трассировочные схемы (без понятия, что это такое). Результат этой регистрации можно проверить командой:
Logman query providers

4. Затем забираемся в корневой каталог этого распакованного архива и в нем выполняем команду:
Logman start MyTrace -pf "control_GUID_files/ctrl.guid.all" -o Out.etl -ets
в результате чего в этом каталоге создастся файл Out.etl, содержащий интересующую нас трассировку.
Тут есть маленькая тонкость. Если эта команда файл ctrl.guid.all не увидит (ну там, путь неправильно указали, или ещё что-то), она сильно не огорчится, но ничего ловить не будет. Поэтому надо внимательно присмотреться, пишет ли она в момент запуска раздел "Поставщики:" , в котором перечисляет guid-ы механизмов, которые будет отслеживать.

5. Запускаем приложение, которое хотим отследить, и пока мы там работаем, в файле из п.4 копится инфа в неудобоваримом виде.

6. Когда по нашему мнению наловится достаточно информации, завершаем трассировку командой:
Logman stop MyTrace -ets

7. Наконец, перегоняем информацию из этого Out.etl во что-то более текстовое командой:
TraceRPT /y Out.etl

Правда, по изучению результата трассировки меня постигло разочарование. Нет, оно там рисует строки подключения, отображает факт обмена с сервером, показывает какие-то адреса и непонятные числа, но и только. Ни одного sql-запроса в явном виде я там не обнаружил.

Этот огорчительный момент вернул меня к старому доброму подслушиванию сети. Обычно я использую Packetyzer, а тут наткнулся на Microsoft Network Monitor 3.4 и решил попробовать его. Там, правда, было написано, что нужно ещё установить дополнительно NMDecrypt, но, по-моему, он ловит всё и так.(*) Для более новых систем есть Microsoft Message Analyzer, но у меня WinXP, так что тут без вариантов. Правда, результатом подслушивания является мешанина данных в кодировке ANSI и UCS-2e, но с этим приходится мириться, главное, что хоть какие-то обрывки получаются в читабельном виде.

* на одной машине потребовалось ещё доустановить Network Monitor Parsers и выбрать в меню Tools - пунт Options - вкладка Parser profiles профиль Default, чтобы программа перестала жаловаться "unable to build conversation"

воскресенье, 8 мая 2016 г.

msiexec: журналирование

Оказывается, при инсталляции пакета .msi можно включить журналирование. Делается это так:
msiexec /i пакет.msi /L*V имя_файла_журнала.log

воскресенье, 10 апреля 2016 г.

vim: небольшая шпаргалка

Vim при запуске читает настройки из файла ~/.vimrc. Если хочется, чтобы для каждого проекта эти настройки были свои, нужно в ~/.vimrc прописать:
set exrc
set secure
Вторая строка тут вроде как отключает в vim возможность выполнять из-под себя команды оболочки.

Так или иначе, теперь vim будет сперва искать и подхватывать файлы .vimrc, находящиеся в текущей директории. Например, с таким содержанием:
syntax off
set shiftwidth=4
set tabwidth=4
set expandtab
Ну перестала мне с каких-то пор нравиться подсветка синтаксиса. Это, конечно, сильно упрощало бы чтение кода и ускоряло бы разработку, но я стал совсем казуалом, так что подобные штуки уже непринципиальны.

вторник, 5 апреля 2016 г.

Windows: сбросить лишние терминальные сессии

Если при попытке прицепиться к терминальному серверу по RDP, например, командой
mstsc /v:myServer
выдаётся ошибка "Превышено максимальное допустимое количество подключений", то остаётся только одно: выкинуть кого-нибудь из уже подключенных пользователей.

Как оказалось, делается это достаточно просто:

1. Авторизуемся на сервере командой:
net use \\myServer

2. Просматриваем список терминальных сессий командой:
qwinsta /server:myServer

3. Прибиваем лишние сеансы командой:
rwinsta /server:myServer <id сессии>

понедельник, 28 марта 2016 г.

Linux: cron и mpg123

Обновился с ubuntu 14.10 до 15.10 - отвалился будильник.

Ну, то есть, в crontab была строчка:
30  07  *  *  1-5  huhmuh   /home/huhmuh/budilnik.sh
которая успешно выполняла скрипт budilnik.sh:
#!/bin/sh
mpg123 -l 0 "/home/huhmuh/budilnik.mp3"
возвращавший меня в реальность каждое утро.

После обновления эта строчка перестала подавать признаки жизни, зато в /var/log/syslog появились такие интересные сообщения:
pulseaudio[12765]: [pulseaudio] source.c: Default and alternate sample rates are the same.
pulseaudio[12765]: [pulseaudio] socket-server.c: bind(): Адрес уже используется
pulseaudio[12765]: [pulseaudio] module.c: Failed to load module "module-esound-protocol-unix" (argument: ""): initialization failed.
pulseaudio[12765]: [pulseaudio] main.c: Module load failed.
pulseaudio[12765]: [pulseaudio] main.c: Не удалось инициализировать демон.
pulseaudio[12762]: [pulseaudio] main.c: Не удалось запустить демон.

Не знаю, что это такое, но побороть как-то удалось. Правда, теперь вышеупомянутая строчка выглядит так:
30  07  *  *  1-5  huhmuh   export XDG_RUNTIME_DIR=/run/user/1000 && /home/huhmuh/budilnik.sh
(конкретное значение переменной окружения XDG_RUNTIME_DIR подсмотрел командой printenv)

понедельник, 25 января 2016 г.

Разбирал тут у себя в почте квитанции о недоставленных сообщениях. Среди привычных "mailbox is full" и "account is disabled" обнаружилось зловещее:
Message was not accepted -- invalid mailbox. Local mailbox XXX@mail.ru is unavailable: user is terminated
Вот прямо даже не знаю, что и подумать...

пятница, 22 января 2016 г.

мало ли, вдруг пригодится...

- Видишь ли, - сказал храбрый Рыцарь своему верному Оруженосцу, - дракона победить совсем не просто. Никто не помнит, когда появились эти зверюги, никто не знает, зачем им принцессы, и никто никогда не видел мертвого дракона. Но есть секрет...

- И какой же? - с интересом спросил Оруженосец.

Рыцарь немного помолчал.

- Он передаётся из поколения в поколение. Я его услышал от своего отца, тому рассказал мой дед, и так далее. Если у дракона количество голов больше количества лап, то это - неправильный дракон. Он с лёгкостью отдаст нам принцессу - а, может даже, и не одну, и будет отдавать их до тех пор, пока не станет правильным драконом. Каждый раз, когда такой дракон выпускает принцессу, число его голов уменьшается на количество его лап. Такая вот интересная закономерность.

- То есть, - оживился Оруженосец, - если мы встретим, например, шестиглавого четырехлапого дракона, то... м-м-м, получим одну принцессу, а дракон станет двуглавым?

- Совершенно верно.

- А если дракон будет тринадцатиглавым и трёхлапым, то мы обзаведёмся гаремом из четырех принцесс, а одноглавый дракон станет правильным? Вот это чудеса!

- Да, если папа не врал. А у меня нет причин ему не верить. По крайней мере, мама и тётя Марта всегда подтверждали его слова.

- Хм-м, постойте, господин, а вдруг дракон окажется, скажем, десятиглавым и пятилапым?

Рыцарь задумался.

- Вообще-то, об этом не принято говорить вслух, но дракон куда-то пропадает, а вместо него мы получаем двух принцесс.

- О-чу-меть! Надо же, какая загадочная зверюга. Прожорливая, небось?

- Ужасно прожорливая. Хорошо, что травоядная. Пощиплет травку, смотришь - а число голов удвоилось, ну и лап тоже. Только что бегал по лужайке на семи ногах и жрал одуванчики в одну пасть, а тут, глядь: уже лап четырнадцать и две башки поглощают флору гораздо шустрее. Или три головы на двадцати одной лапе... Зависит, в общем, от объёма переваренного силоса.

- И что же, никак его не убить?

- Никак. Разве что сократить, да и то не всех.

- Не понял, господин. Это как "сократить"?

- Ну, сам я не пробовал. Но Жан - ты знаешь его, он живёт во дворце на Серых Холмах - рассказывал, что однажды на него напал огромный двуглавый двенадцатилапый дракон. И что делать - непонятно. Убить нельзя, убежать - тоже како-то несолидно. Тогда Жан достаёт свой меч-кладенец, посильнее размахивается, и р-р-аз! отполовинивает число голов. Тут же каким-то чудом число ног тоже уменьшается в два раза, и чудовище, ставшее одноглавым шестилапым - не больше таракана - шустро исчезает в ближайших кустах.

- И это у всех так?

- Да, во сколько раз уменьшается число голов, во столько раз становится меньше число ног.

... Будильник трещал не переставая. Иванов с трудом оторвал чугунную голову от подушки, оказавшейся учебником математики за 6-й класс, но ещё не до конца понимая, откуда в его спальне парты, и почему он в школе, и куда все подевались. Будильник постепенно превратился в школьный звонок и заткнулся. Иванов протёр глаза, сладко потянулся и понял, что за окном вечер и пора идти домой. В пустой башке продолжали ворочаться рыцари и какие-то неправильные сократимые драконы. Иванов подхватил портфель. сунул в него свою "подушку" и, продолжая думать над тайнами высшей математики, вышел из класса.

понедельник, 18 января 2016 г.

MS Excel - посчитать разность дат

Ексель полон сюрпризов. Помнится, то ли в 2000, то ли в 2003 там в качестве пасхалки присутствовали даже гоночки. А теперь это.

Потребовалось рассчитать возраст человека на определенную дату. На первый взгляд, подходящей функции в екселе нет. Однако, оказывается, это не так:
=РАЗНДАТ(B1;A1;"y") - количество полных лет
=РАЗНДАТ(B1;A1;"ym") - количество полных месяцев свыше полных лет
=РАЗНДАТ(B1;A1;"md") - количество дней свыше полных месяцев

Подсмотрел тут:
http://www.planetaexcel.ru/techniques/6/105/
http://www.excel-office.ru/formulivexcel/schitaemvosrastvexcel